/* -------------------------------------------------------------------------
 *
 * execQual.cpp
 *	  Routines to evaluate qualification and targetlist expressions
 *
 * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
 * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/gausskernel/runtime/executor/execQual.cpp
 *
 * -------------------------------------------------------------------------
 */
/*
 *	 INTERFACE ROUTINES
 *		ExecEvalExpr	- (now a macro) evaluate an expression, return a datum
 *		ExecEvalExprSwitchContext - same, but switch into eval memory context
 *		ExecQual		- return true/false if qualification is satisfied
 *		ExecProject		- form a new tuple by projecting the given tuple
 *
 *	 NOTES
 *		The more heavily used ExecEvalExpr routines, such as ExecEvalScalarVar,
 *		are hotspots. Making these faster will speed up the entire system.
 *
 *		ExecProject() is used to make tuple projections.  Rather then
 *		trying to speed it up, the execution plan should be pre-processed
 *		to facilitate attribute sharing between nodes wherever possible,
 *		instead of doing needless copying.	-cim 5/31/91
 *
 *		During expression evaluation, we check_stack_depth only in
 *		ExecMakeFunctionResult (and substitute routines) rather than at every
 *		single node.  This is a compromise that trades off precision of the
 *		stack limit setting to gain speed.
 */
#include "postgres.h"
#include "knl/knl_variable.h"

#include "access/nbtree.h"
#include "access/tupconvert.h"
#include "access/tableam.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
#include "executor/exec/execdebug.h"
#include "executor/node/nodeSubplan.h"
#include "executor/node/nodeAgg.h"
#include "executor/node/nodeCtescan.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "plugin_optimizer/planner.h"
#include "plugin_parser/parse_coerce.h"
#include "pgstat.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
#include "utils/xml.h"
#include "access/hash.h"
#include "access/transam.h"
#ifdef PGXC
#include "pgxc/groupmgr.h"
#include "pgxc/pgxc.h"
#endif
#include "optimizer/streamplan.h"
#include "gstrace/gstrace_infra.h"
#include "gstrace/executer_gstrace.h"
#include "commands/trigger.h"
#include "db4ai/gd.h"
#include "catalog/pg_proc_fn.h"
#include "access/tuptoaster.h"
#include "plugin_parser/parse_expr.h"

/* static function decls */
static bool isAssignmentIndirectionExpr(ExprState* exprstate);
static Datum ExecEvalAggref(AggrefExprState* aggref, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalWindowFunc(WindowFuncExprState* wfunc, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalScalarVar(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalScalarVarFast(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalWholeRowVar(
    WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalWholeRowFast(
    WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalWholeRowSlow(
    WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalConst(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalParamExec(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalParamExtern(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static bool isVectorEngineSupportSetFunc(Oid funcid);
template <bool vectorized>
static void init_fcache(
    Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt, bool needDescForSets);
static void ShutdownFuncExpr(Datum arg);
static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod, TupleDesc* cache_field, ExprContext* econtext);
static void ShutdownTupleDescRef(Datum arg);

template <bool has_refcursor>
static ExprDoneCond ExecEvalFuncArgs(
    FunctionCallInfo fcinfo, List* argList, ExprContext* econtext, int* plpgsql_var_dno = NULL);
static void ExecPrepareTuplestoreResult(
    FuncExprState* fcache, ExprContext* econtext, Tuplestorestate* resultStore, TupleDesc resultDesc);
static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);

template <bool has_refcursor, bool has_cursor_return, bool isSetReturnFunc>
static Datum ExecMakeFunctionResult(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);

template <bool has_refcursor, bool has_cursor_return>
static Datum ExecMakeFunctionResultNoSets(
    FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalFunc(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalOper(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalDistinct(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalScalarArrayOp(
    ScalarArrayOpExprState* sstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalNot(BoolExprState* notclause, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalOr(BoolExprState* orExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalAnd(BoolExprState* andExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalConvertRowtype(
    ConvertRowtypeExprState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalCase(CaseExprState* caseExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalCaseTestExpr(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalArray(ArrayExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalRow(RowExprState* rstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalRowCompare(RowCompareExprState* rstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalCoalesce(
    CoalesceExprState* coalesceExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalMinMax(MinMaxExprState* minmaxExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalXml(XmlExprState* xmlExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalNullIf(FuncExprState* nullIfExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalNullTest(NullTestState* nstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalHashFilter(HashFilterState* hstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalBooleanTest(GenericExprState* bstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalCoerceToDomain(
    CoerceToDomainState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalCoerceToDomainValue(
    ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalFieldSelect(FieldSelectState* fstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalFieldStore(FieldStoreState* fstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalRelabelType(
    GenericExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalCoerceViaIO(CoerceViaIOState* iostate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalArrayCoerceExpr(
    ArrayCoerceExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalCurrentOfExpr(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalGroupingFuncExpr(
    GroupingFuncExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static Datum ExecEvalGroupingIdExpr(
    GroupingIdExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
static bool func_has_refcursor_args(Oid Funcid, FunctionCallInfoData* fcinfo);
extern struct varlena *heap_tuple_fetch_and_copy(Relation rel, struct varlena *attr, bool needcheck);
static void check_huge_clob_paramter(FunctionCallInfoData* fcinfo, bool is_have_huge_clob);

THR_LOCAL PLpgSQL_execstate* plpgsql_estate = NULL;

/* ----------------------------------------------------------------
 *		ExecEvalExpr routines
 *
 *		Recursively evaluate a targetlist or qualification expression.
 *
 * Each of the following routines having the signature
 *		Datum ExecEvalFoo(ExprState *expression,
 *						  ExprContext *econtext,
 *						  bool *isNull,
 *						  ExprDoneCond *isDone);
 * is responsible for evaluating one type or subtype of ExprState node.
 * They are normally called via the ExecEvalExpr macro, which makes use of
 * the function pointer set up when the ExprState node was built by
 * ExecInitExpr.  (In some cases, we change this pointer later to avoid
 * re-executing one-time overhead.)
 *
 * Note: for notational simplicity we declare these functions as taking the
 * specific type of ExprState that they work on.  This requires casting when
 * assigning the function pointer in ExecInitExpr.	Be careful that the
 * function signature is declared correctly, because the cast suppresses
 * automatic checking!
 *
 *
 * All these functions share this calling convention:
 *
 * Inputs:
 *		expression: the expression state tree to evaluate
 *		econtext: evaluation context information
 *
 * Outputs:
 *		return value: Datum value of result
 *		*isNull: set to TRUE if result is NULL (actual return value is
 *				 meaningless if so); set to FALSE if non-null result
 *		*isDone: set to indicator of set-result status
 *
 * A caller that can only accept a singleton (non-set) result should pass
 * NULL for isDone; if the expression computes a set result then an error
 * will be reported via ereport.  If the caller does pass an isDone pointer
 * then *isDone is set to one of these three states:
 *		ExprSingleResult		singleton result (not a set)
 *		ExprMultipleResult		return value is one element of a set
 *		ExprEndResult			there are no more elements in the set
 * When ExprMultipleResult is returned, the caller should invoke
 * ExecEvalExpr() repeatedly until ExprEndResult is returned.  ExprEndResult
 * is returned after the last real set element.  For convenience isNull will
 * always be set TRUE when ExprEndResult is returned, but this should not be
 * taken as indicating a NULL element of the set.  Note that these return
 * conventions allow us to distinguish among a singleton NULL, a NULL element
 * of a set, and an empty set.
 *
 * The caller should already have switched into the temporary memory
 * context econtext->ecxt_per_tuple_memory.  The convenience entry point
 * ExecEvalExprSwitchContext() is provided for callers who don't prefer to
 * do the switch in an outer loop.	We do not do the switch in these routines
 * because it'd be a waste of cycles during nested expression evaluation.
 * ----------------------------------------------------------------
 */
/* ----------
 *	  ExecEvalArrayRef
 *
 *	   This function takes an ArrayRef and returns the extracted Datum
 *	   if it's a simple reference, or the modified array value if it's
 *	   an array assignment (i.e., array element or slice insertion).
 *
 * NOTE: if we get a NULL result from a subscript expression, we return NULL
 * when it's an array reference, or raise an error when it's an assignment.
 *
 * NOTE: we deliberately refrain from applying DatumGetArrayTypeP() here,
 * even though that might seem natural, because this code needs to support
 * both varlena arrays and fixed-length array types.  DatumGetArrayTypeP()
 * only works for the varlena kind.  The routines we call in arrayfuncs.c
 * have to know the difference (that's what they need refattrlength for).
 * ----------
 */
Datum ExecEvalArrayRef(ArrayRefExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    ArrayRef* arrayRef = (ArrayRef*)astate->xprstate.expr;
    ArrayType* array_source = NULL;
    ArrayType* resultArray = NULL;
    bool isAssignment = (arrayRef->refassgnexpr != NULL);
    bool eisnull = false;
    ListCell* l = NULL;
    int i = 0;
    int j = 0;
    IntArray upper, lower;
    int* lIndex = NULL;
    Oid typOid = astate->xprstate.resultType;

    array_source = (ArrayType*)DatumGetPointer(ExecEvalExpr(astate->refexpr, econtext, isNull, isDone));
    /*
     * If refexpr yields NULL, and it's a fetch, then result is NULL. In the
     * assignment case, we'll cons up something below.
     */
    if (*isNull) {
        if (isDone && *isDone == ExprEndResult)
            return (Datum)NULL; /* end of set result */
        if (!isAssignment)
            return (Datum)NULL;
    }
    int returnNestTableLayer = 0;
    ExecTableOfIndexInfo execTableOfIndexInfo;
    initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext);
    ExecEvalParamExternTableOfIndex((Node*)astate->refexpr->expr, &execTableOfIndexInfo);
    if (u_sess->SPI_cxt.cur_tableof_index != NULL) {
        u_sess->SPI_cxt.cur_tableof_index->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType;
        u_sess->SPI_cxt.cur_tableof_index->tableOfIndex = execTableOfIndexInfo.tableOfIndex;
        u_sess->SPI_cxt.cur_tableof_index->tableOfGetNestLayer = list_length(astate->refupperindexpr);
    }

    foreach (l, astate->refupperindexpr) {
        ExprState* eltstate = (ExprState*)lfirst(l);

        if (i >= MAXDIM)
            ereport(ERROR,
                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                    errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", i + 1, MAXDIM)));
        if (OidIsValid(execTableOfIndexInfo.tableOfIndexType) || execTableOfIndexInfo.isnestedtable) {
            bool isTran = false;
            PLpgSQL_execstate* old_estate = plpgsql_estate;
            Datum exprValue = (Datum)ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
            plpgsql_estate = old_estate;
            if (unlikely(execTableOfIndexInfo.tableOfIndexType))
            if (execTableOfIndexInfo.tableOfIndexType == VARCHAROID && !eisnull && VARATT_IS_1B(exprValue)) {
                exprValue = transVaratt1BTo4B(exprValue);
                isTran = true;
            }
            TableOfIndexKey key;
            PLpgSQL_var* node = NULL;
            key.exprtypeid = execTableOfIndexInfo.tableOfIndexType;
            key.exprdatum = exprValue;
            int index = getTableOfIndexByDatumValue(key, execTableOfIndexInfo.tableOfIndex, &node);
            if (isTran) {
                pfree(DatumGetPointer(exprValue));
            }
            if (execTableOfIndexInfo.isnestedtable) {
                /* for nested table, we should take inner table's array and skip current indx */
                if (node == NULL || index == -1) {
                    eisnull = true;
                } else {
                    PLpgSQL_var* var = node;
                    execTableOfIndexInfo.isnestedtable  = (var->nest_table != NULL);
                    array_source = (ArrayType*)DatumGetPointer(var->value);
                    execTableOfIndexInfo.tableOfIndexType = var->datatype->tableOfIndexType;
                    execTableOfIndexInfo.tableOfIndex = var->tableOfIndex;
                    eisnull = var->isnull;
                    returnNestTableLayer = var->nest_layers;
                    if (plpgsql_estate)
                        plpgsql_estate->curr_nested_table_type = var->datatype->typoid;
                    continue;
                }
            } else {
                returnNestTableLayer = 0;
            }
            if (index == -1) {
                eisnull = true;
            } else {
                upper.indx[i++] = index;
            }
        } else {
            PLpgSQL_execstate* old_estate = plpgsql_estate;
            upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, &eisnull, NULL));
            plpgsql_estate = old_estate;
            returnNestTableLayer = 0;
        }
        
        /* If any index expr yields NULL, result is NULL or error */
        if (eisnull) {
            if (isAssignment)
                ereport(ERROR,
                    (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                        errmsg("array subscript in assignment must not be null")));
            *isNull = true;
            return (Datum)NULL;
        }
    }

    if (astate->reflowerindexpr != NIL) {
        foreach (l, astate->reflowerindexpr) {
            ExprState* eltstate = (ExprState*)lfirst(l);

            if (j >= MAXDIM)
                ereport(ERROR,
                    (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                        errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", j + 1, MAXDIM)));

            if (execTableOfIndexInfo.tableOfIndexType == VARCHAROID || execTableOfIndexInfo.isnestedtable) {
                ereport(ERROR,
                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                            errmsg("index by varchar or nested table don't support two subscripts")));
            } else {
                PLpgSQL_execstate* old_estate = plpgsql_estate;
                lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, &eisnull, NULL));
                plpgsql_estate = old_estate;
            }
            /* If any index expr yields NULL, result is NULL or error */
            if (eisnull) {
                if (isAssignment)
                    ereport(ERROR,
                        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                            errmsg("array subscript in assignment must not be null")));
                *isNull = true;
                return (Datum)NULL;
            }
        }
        /* this can't happen unless parser messed up */
        if (i != j)
            ereport(ERROR,
                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
                    errmodule(MOD_EXECUTOR),
                    (errmsg("upper and lower index lists are not same length (%d, %d)", i, j))));
        lIndex = lower.indx;
    } else
        lIndex = NULL;

    if (isAssignment) {
        Datum sourceData;
        Datum save_datum;
        bool save_isNull = false;

        /*
         * We might have a nested-assignment situation, in which the
         * refassgnexpr is itself a FieldStore or ArrayRef that needs to
         * obtain and modify the previous value of the array element or slice
         * being replaced.	If so, we have to extract that value from the
         * array and pass it down via the econtext's caseValue.  It's safe to
         * reuse the CASE mechanism because there cannot be a CASE between
         * here and where the value would be needed, and an array assignment
         * can't be within a CASE either.  (So saving and restoring the
         * caseValue is just paranoia, but let's do it anyway.)
         *
         * Since fetching the old element might be a nontrivial expense, do it
         * only if the argument appears to actually need it.
         */
        save_datum = econtext->caseValue_datum;
        save_isNull = econtext->caseValue_isNull;

        if (isAssignmentIndirectionExpr(astate->refassgnexpr)) {
            if (*isNull) {
                /* whole array is null, so any element or slice is too */
                econtext->caseValue_datum = (Datum)0;
                econtext->caseValue_isNull = true;
            } else if (lIndex == NULL) {
                econtext->caseValue_datum = array_ref(array_source,
                    i,
                    upper.indx,
                    astate->refattrlength,
                    astate->refelemlength,
                    astate->refelembyval,
                    astate->refelemalign,
                    &econtext->caseValue_isNull);
            } else {
                resultArray = array_get_slice(array_source,
                    i,
                    upper.indx,
                    lower.indx,
                    astate->refattrlength,
                    astate->refelemlength,
                    astate->refelembyval,
                    astate->refelemalign);
                econtext->caseValue_datum = PointerGetDatum(resultArray);
                econtext->caseValue_isNull = false;
            }
        } else {
            /* argument shouldn't need caseValue, but for safety set it null */
            econtext->caseValue_datum = (Datum)0;
            econtext->caseValue_isNull = true;
        }

        /*
         * Evaluate the value to be assigned into the array.
         */
        sourceData = ExecEvalExpr(astate->refassgnexpr, econtext, &eisnull, NULL);

        econtext->caseValue_datum = save_datum;
        econtext->caseValue_isNull = save_isNull;
        
        /*
         * For an assignment to a fixed-length array type, both the original
         * array and the value to be assigned into it must be non-NULL, else
         * we punt and return the original array.
         */
        if (astate->refattrlength > 0) /* fixed-length array? */
            if (eisnull || *isNull)
                return PointerGetDatum(array_source);

        /*
         * For assignment to varlena arrays, we handle a NULL original array
         * by substituting an empty (zero-dimensional) array; insertion of the
         * new element will result in a singleton array value.	It does not
         * matter whether the new element is NULL.
         */
        if (*isNull) {
            array_source = construct_empty_array(arrayRef->refelemtype);
            *isNull = false;
        }

        if (lIndex == NULL)
            resultArray = array_set(array_source,
                i,
                upper.indx,
                sourceData,
                eisnull,
                astate->refattrlength,
                astate->refelemlength,
                astate->refelembyval,
                astate->refelemalign);
        else
            resultArray = array_set_slice(array_source,
                i,
                upper.indx,
                lower.indx,
                (ArrayType*)DatumGetPointer(sourceData),
                eisnull,
                astate->refattrlength,
                astate->refelemlength,
                astate->refelembyval,
                astate->refelemalign);
        return PointerGetDatum(resultArray);
    }
    /* for nested table, if get inner table's elem, need cover elem type */
    if (list_length(astate->refupperindexpr) > i && i > 0 && plpgsql_estate) {
        if (plpgsql_estate->curr_nested_table_type != typOid) {
            plpgsql_estate->curr_nested_table_type = ARR_ELEMTYPE(array_source);
            get_typlenbyvalalign(plpgsql_estate->curr_nested_table_type,
                                &astate->refelemlength,
                                &astate->refelembyval,
                                &astate->refelemalign);
        }
    }
    if (plpgsql_estate) {
        plpgsql_estate->curr_nested_table_layers = returnNestTableLayer;
    }
    if (lIndex == NULL) {
        if (unlikely(i == 0)) {
            /* get nested table's inner table */
            *isNull = eisnull;
            return (Datum)array_source;
        } else {
            return array_ref(array_source,
                i,
                upper.indx,
                astate->refattrlength,
                astate->refelemlength,
                astate->refelembyval,
                astate->refelemalign,
                isNull);
        }
    } else {
        resultArray = array_get_slice(array_source,
            i,
            upper.indx,
            lower.indx,
            astate->refattrlength,
            astate->refelemlength,
            astate->refelembyval,
            astate->refelemalign);
        return PointerGetDatum(resultArray);
    }
}

/*
 * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef
 * that might need the old element value passed down?
 *
 * (We could use this in ExecEvalFieldStore too, but in that case passing
 * the old value is so cheap there's no need.)
 */
static bool isAssignmentIndirectionExpr(ExprState* exprstate)
{
    if (exprstate == NULL)
        return false; /* just paranoia */
    if (IsA(exprstate, FieldStoreState)) {
        FieldStore* fstore = (FieldStore*)exprstate->expr;

        if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
            return true;
    } else if (IsA(exprstate, ArrayRefExprState)) {
        ArrayRef* arrayRef = (ArrayRef*)exprstate->expr;

        if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
            return true;
    }
    return false;
}

/* ----------------------------------------------------------------
 *		ExecEvalAggref
 *
 *		Returns a Datum whose value is the value of the precomputed
 *		aggregate found in the given expression context.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalAggref(AggrefExprState* aggref, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    if (isDone != NULL)
        *isDone = ExprSingleResult;

    if (econtext->ecxt_aggvalues == NULL) /* safety check */
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_AGG),
                errmodule(MOD_EXECUTOR),
                errmsg("no aggregates in this expression context")));

    *isNull = econtext->ecxt_aggnulls[aggref->aggno];
    return econtext->ecxt_aggvalues[aggref->aggno];
}

/* ----------------------------------------------------------------
 *		ExecEvalWindowFunc
 *
 *		Returns a Datum whose value is the value of the precomputed
 *		window function found in the given expression context.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalWindowFunc(WindowFuncExprState* wfunc, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    if (isDone != NULL)
        *isDone = ExprSingleResult;

    if (econtext->ecxt_aggvalues == NULL) /* safety check */
        ereport(ERROR,
            (errcode(ERRCODE_WINDOWING_ERROR),
                errmodule(MOD_EXECUTOR),
                errmsg("no window functions in this expression context")));

    *isNull = econtext->ecxt_aggnulls[wfunc->wfuncno];
    return econtext->ecxt_aggvalues[wfunc->wfuncno];
}

/* ----------------------------------------------------------------
 *		ExecEvalScalarVar
 *
 *		Returns a Datum whose value is the value of a scalar (not whole-row)
 *		range variable with respect to given expression context.
 *
 * Note: ExecEvalScalarVar is executed only the first time through in a given
 * plan; it changes the ExprState's function pointer to pass control directly
 * to ExecEvalScalarVarFast after making one-time checks.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalScalarVar(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    Var* variable = (Var*)exprstate->expr;
    TupleTableSlot* slot = NULL;
    AttrNumber attnum;

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    /* Get the input slot and attribute number we want */
    switch (variable->varno) {
        case INNER_VAR: /* get the tuple from the inner node */
            slot = econtext->ecxt_innertuple;
            break;

        case OUTER_VAR: /* get the tuple from the outer node */
            slot = econtext->ecxt_outertuple;
            break;

            /* INDEX_VAR is handled by default case */
        default: /* get the tuple from the relation being scanned */
            slot = econtext->ecxt_scantuple;
            break;
    }

    attnum = variable->varattno;

    /* This was checked by ExecInitExpr */
    Assert(attnum != InvalidAttrNumber);

    RightRefState* refState = econtext->rightRefState;
    int index = attnum - 1;
    if (IS_ENABLE_INSERT_RIGHT_REF(refState) ||
       (IS_ENABLE_UPSERT_RIGHT_REF(refState) && refState->hasExecs[index] && index < refState->colCnt)) {
        *isNull = refState->isNulls[index];
        return refState->values[index];
    }

    if (slot == nullptr) {
        ereport(ERROR,(errcode(ERRCODE_INVALID_ATTRIBUTE), errmodule(MOD_EXECUTOR),
                        errmsg("attribute number %d does not exists.", attnum)));
    }
    
    /*
     * If it's a user attribute, check validity (bogus system attnums will be
     * caught inside table's getattr).  What we have to check for here is the
     * possibility of an attribute having been changed in type since the plan
     * tree was created.  Ideally the plan will get invalidated and not
     * re-used, but just in case, we keep these defenses.  Fortunately it's
     * sufficient to check once on the first time through.
     *
     * Note: we allow a reference to a dropped attribute.  table's getattr will
     * force a NULL result in such cases.
     *
     * Note: ideally we'd check typmod as well as typid, but that seems
     * impractical at the moment: in many cases the tupdesc will have been
     * generated by ExecTypeFromTL(), and that can't guarantee to generate an
     * accurate typmod in all cases, because some expression node types don't
     * carry typmod.
     */
    if (attnum > 0) {
        TupleDesc slot_tupdesc = slot->tts_tupleDescriptor;
        Form_pg_attribute attr;

        if (attnum > slot_tupdesc->natts) /* should never happen */
            ereport(ERROR,
                (errcode(ERRCODE_INVALID_ATTRIBUTE),
                    errmodule(MOD_EXECUTOR),
                    errmsg("attribute number %d exceeds number of columns %d", attnum, slot_tupdesc->natts)));

        attr = slot_tupdesc->attrs[attnum - 1];

        /* can't check type if dropped, since atttypid is probably 0 */
        if (!attr->attisdropped) {
            if (variable->vartype != attr->atttypid)
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_ATTRIBUTE),
                        errmodule(MOD_EXECUTOR),
                        errmsg("attribute %d has wrong type", attnum),
                        errdetail("Table has type %s, but query expects %s.",
                            format_type_be(attr->atttypid),
                            format_type_be(variable->vartype))));
        }
    }

    /* Skip the checking on future executions of node */
    exprstate->evalfunc = ExecEvalScalarVarFast;

    /* Fetch the value from the slot */
    return tableam_tslot_getattr(slot, attnum, isNull);
}

/* ----------------------------------------------------------------
 *		ExecEvalScalarVarFast
 *
 *		Returns a Datum for a scalar variable.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalScalarVarFast(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    Var* variable = (Var*)exprstate->expr;
    TupleTableSlot* slot = NULL;
    AttrNumber attnum;

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    /* Get the input slot and attribute number we want */
    switch (variable->varno) {
        case INNER_VAR: /* get the tuple from the inner node */
            slot = econtext->ecxt_innertuple;
            break;

        case OUTER_VAR: /* get the tuple from the outer node */
            slot = econtext->ecxt_outertuple;
            break;

            /* INDEX_VAR is handled by default case */
        default: /* get the tuple from the relation being scanned */
            slot = econtext->ecxt_scantuple;
            break;
    }

    attnum = variable->varattno;

    Assert(slot != NULL);
    /* Fetch the value from the slot */
    return tableam_tslot_getattr(slot, attnum, isNull);
}

/* ----------------------------------------------------------------
 *		ExecEvalWholeRowVar
 *
 *		Returns a Datum whose value is the value of a whole-row range
 *		variable with respect to given expression context.
 *
 * Note: ExecEvalWholeRowVar is executed only the first time through in a
 * given plan; it changes the ExprState's function pointer to pass control
 * directly to ExecEvalWholeRowFast or ExecEvalWholeRowSlow after making
 * one-time checks.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalWholeRowVar(
    WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    Var* variable = (Var*)wrvstate->xprstate.expr;
    TupleTableSlot* slot = NULL;
    bool needslow = false;

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    /* This was checked by ExecInitExpr */
    Assert(variable->varattno == InvalidAttrNumber);

    /* Get the input slot we want */
    switch (variable->varno) {
        case INNER_VAR: /* get the tuple from the inner node */
            slot = econtext->ecxt_innertuple;
            break;

        case OUTER_VAR: /* get the tuple from the outer node */
            slot = econtext->ecxt_outertuple;
            break;

            /* INDEX_VAR is handled by default case */
        default: /* get the tuple from the relation being scanned */
            slot = econtext->ecxt_scantuple;
            break;
    }

    /*
     * If the input tuple came from a subquery, it might contain "resjunk"
     * columns (such as GROUP BY or ORDER BY columns), which we don't want to
     * keep in the whole-row result.  We can get rid of such columns by
     * passing the tuple through a JunkFilter --- but to make one, we have to
     * lay our hands on the subquery's targetlist.  Fortunately, there are not
     * very many cases where this can happen, and we can identify all of them
     * by examining our parent PlanState.  We assume this is not an issue in
     * standalone expressions that don't have parent plans.  (Whole-row Vars
     * can occur in such expressions, but they will always be referencing
     * table rows.)
     */
    if (wrvstate->parent) {
        PlanState* subplan = NULL;

        switch (nodeTag(wrvstate->parent)) {
            case T_SubqueryScanState:
                subplan = ((SubqueryScanState*)wrvstate->parent)->subplan;
                break;
            case T_CteScanState:
                subplan = ((CteScanState*)wrvstate->parent)->cteplanstate;
                break;
            default:
                break;
        }

        if (subplan != NULL) {
            bool junk_filter_needed = false;
            ListCell* tlist = NULL;

            /* Detect whether subplan tlist actually has any junk columns */
            foreach (tlist, subplan->plan->targetlist) {
                TargetEntry* tle = (TargetEntry*)lfirst(tlist);

                if (tle->resjunk) {
                    junk_filter_needed = true;
                    break;
                }
            }

            /* If so, build the junkfilter in the query memory context */
            if (junk_filter_needed) {
                MemoryContext oldcontext;

                oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
                wrvstate->wrv_junkFilter = ExecInitJunkFilter(subplan->plan->targetlist,
                    ExecGetResultType(subplan)->tdhasoid,
                    ExecInitExtraTupleSlot(wrvstate->parent->state));
                MemoryContextSwitchTo(oldcontext);
            }
        }
    }

    /* Apply the junkfilter if any */
    if (wrvstate->wrv_junkFilter != NULL)
        slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);

    /*
     * If the Var identifies a named composite type, we must check that the
     * actual tuple type is compatible with it.
     */
    if (variable->vartype != RECORDOID) {
        TupleDesc var_tupdesc;
        TupleDesc slot_tupdesc;
        int i;

        /*
         * We really only care about numbers of attributes and data types.
         * Also, we can ignore type mismatch on columns that are dropped in
         * the destination type, so long as (1) the physical storage matches
         * or (2) the actual column value is NULL.	Case (1) is helpful in
         * some cases involving out-of-date cached plans, while case (2) is
         * expected behavior in situations such as an INSERT into a table with
         * dropped columns (the planner typically generates an INT4 NULL
         * regardless of the dropped column type).	If we find a dropped
         * column and cannot verify that case (1) holds, we have to use
         * ExecEvalWholeRowSlow to check (2) for each row.
         */
        var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);

        slot_tupdesc = slot->tts_tupleDescriptor;

        if (var_tupdesc->natts != slot_tupdesc->natts)
            ereport(ERROR,
                (errcode(ERRCODE_DATATYPE_MISMATCH),
                    errmsg("table row type and query-specified row type do not match"),
                    errdetail_plural("Table row contains %d attribute, but query expects %d.",
                        "Table row contains %d attributes, but query expects %d.",
                        slot_tupdesc->natts,
                        slot_tupdesc->natts,
                        var_tupdesc->natts)));

        for (i = 0; i < var_tupdesc->natts; i++) {
            Form_pg_attribute vattr = var_tupdesc->attrs[i];
            Form_pg_attribute sattr = slot_tupdesc->attrs[i];

            if (vattr->atttypid == sattr->atttypid)
                continue; /* no worries */
            if (!vattr->attisdropped)
                ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                        errmsg("table row type and query-specified row type do not match"),
                        errdetail("Table has type %s at ordinal position %d, but query expects %s.",
                            format_type_be(sattr->atttypid),
                            i + 1,
                            format_type_be(vattr->atttypid))));

            if (vattr->attlen != sattr->attlen || vattr->attalign != sattr->attalign)
                needslow = true; /* need runtime check for null */
        }

        ReleaseTupleDesc(var_tupdesc);
    }

    /* Skip the checking on future executions of node */
    if (needslow)
        wrvstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalWholeRowSlow;
    else
        wrvstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalWholeRowFast;

    /* Fetch the value */
    return (*wrvstate->xprstate.evalfunc)((ExprState*)wrvstate, econtext, isNull, isDone);
}

/* ----------------------------------------------------------------
 *		ExecEvalWholeRowFast
 *
 *		Returns a Datum for a whole-row variable.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalWholeRowFast(
    WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    Var* variable = (Var*)wrvstate->xprstate.expr;
    TupleTableSlot* slot = NULL;
    TupleDesc slot_tupdesc;
    HeapTuple tuple;
    TupleDesc tupleDesc;
    HeapTupleHeader dtuple;
    errno_t rc = EOK;

    if (isDone != NULL)
        *isDone = ExprSingleResult;
    *isNull = false;

    /* Get the input slot we want */
    switch (variable->varno) {
        case INNER_VAR: /* get the tuple from the inner node */
            slot = econtext->ecxt_innertuple;
            break;

        case OUTER_VAR: /* get the tuple from the outer node */
            slot = econtext->ecxt_outertuple;
            break;

            /* INDEX_VAR is handled by default case */
        default: /* get the tuple from the relation being scanned */
            slot = econtext->ecxt_scantuple;
            break;
    }

    /* Apply the junkfilter if any */
    if (wrvstate->wrv_junkFilter != NULL)
        slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);

    /*
     * If it's a RECORD Var, we'll use the slot's type ID info.  It's likely
     * that the slot's type is also RECORD; if so, make sure it's been
     * "blessed", so that the Datum can be interpreted later.
     */        
    slot_tupdesc = slot->tts_tupleDescriptor;
    if (variable->vartype == RECORDOID) {
        if (slot_tupdesc->tdtypeid == RECORDOID && slot_tupdesc->tdtypmod < 0)
            assign_record_type_typmod(slot_tupdesc);
    }

    tuple = ExecFetchSlotTuple(slot);
    tupleDesc = slot->tts_tupleDescriptor;
    /*
     * If it's a RECORD Var, we'll use the slot's type ID info.  It's likely
     * that the slot's type is also RECORD; if so, make sure it's been
     * "blessed", so that the Datum can be interpreted later.  (Note: we must
     * do this here, not in ExecEvalWholeRowVar, because some plan trees may
     * return different slots at different times.  We have to be ready to
     * bless additional slots during the run.)
     */
    if (variable->vartype == RECORDOID &&
        tupleDesc->tdtypeid == RECORDOID &&
        tupleDesc->tdtypmod < 0)
        assign_record_type_typmod(tupleDesc);

    /*
     * We have to make a copy of the tuple so we can safely insert the Datum
     * overhead fields, which are not set in on-disk tuples.
     */
    dtuple = (HeapTupleHeader)palloc(tuple->t_len);
    rc = memcpy_s((char*)dtuple, tuple->t_len, (char*)tuple->t_data, tuple->t_len);
    securec_check(rc, "\0", "\0");

    HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len);

    /*
     * If the Var identifies a named composite type, label the tuple with that
     * type; otherwise use what is in the tupleDesc.
     */
    if (variable->vartype != RECORDOID) {
        HeapTupleHeaderSetTypeId(dtuple, variable->vartype);
        HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod);
    } else {
        HeapTupleHeaderSetTypeId(dtuple, tupleDesc->tdtypeid);
        HeapTupleHeaderSetTypMod(dtuple, tupleDesc->tdtypmod);
    }

    return PointerGetDatum(dtuple);
}

/* ----------------------------------------------------------------
 *		ExecEvalWholeRowSlow
 *
 *		Returns a Datum for a whole-row variable, in the "slow" case where
 *		we can't just copy the subplan's output.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalWholeRowSlow(
    WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    Var* variable = (Var*)wrvstate->xprstate.expr;
    TupleTableSlot* slot = NULL;
    HeapTuple tuple;
    TupleDesc tupleDesc;
    TupleDesc var_tupdesc;
    HeapTupleHeader dtuple;
    int i;
    errno_t rc = EOK;

    if (isDone != NULL)
        *isDone = ExprSingleResult;
    *isNull = false;

    /* Get the input slot we want */
    switch (variable->varno) {
        case INNER_VAR: /* get the tuple from the inner node */
            slot = econtext->ecxt_innertuple;
            break;

        case OUTER_VAR: /* get the tuple from the outer node */
            slot = econtext->ecxt_outertuple;
            break;

            /* INDEX_VAR is handled by default case */
        default: /* get the tuple from the relation being scanned */
            slot = econtext->ecxt_scantuple;
            break;
    }

    /* Apply the junkfilter if any */
    if (wrvstate->wrv_junkFilter != NULL)
        slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);

    tuple = ExecFetchSlotTuple(slot);
    tupleDesc = slot->tts_tupleDescriptor;

    Assert(variable->vartype != RECORDOID);
    var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);

    /* Check to see if any dropped attributes are non-null */
    for (i = 0; i < var_tupdesc->natts; i++) {
        Form_pg_attribute vattr = var_tupdesc->attrs[i];
        Form_pg_attribute sattr = tupleDesc->attrs[i];

        if (!vattr->attisdropped)
            continue; /* already checked non-dropped cols */
        if (tableam_tops_tuple_attisnull(tuple, i + 1, tupleDesc))
            continue; /* null is always okay */
        if (vattr->attlen != sattr->attlen || vattr->attalign != sattr->attalign)
            ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH),
                    errmsg("table row type and query-specified row type do not match"),
                    errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.", i + 1)));
    }

    /*
     * We have to make a copy of the tuple so we can safely insert the Datum
     * overhead fields, which are not set in on-disk tuples.
     */
    dtuple = (HeapTupleHeader)palloc(tuple->t_len);
    rc = memcpy_s((char*)dtuple, tuple->t_len, (char*)tuple->t_data, tuple->t_len);
    securec_check(rc, "\0", "\0");

    HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len);
    HeapTupleHeaderSetTypeId(dtuple, variable->vartype);
    HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod);

    ReleaseTupleDesc(var_tupdesc);

    return PointerGetDatum(dtuple);
}

/* ----------------------------------------------------------------
 *		ExecEvalConst
 *
 *		Returns the value of a constant.
 *
 *		Note that for pass-by-ref datatypes, we return a pointer to the
 *		actual constant node.  This is one of the reasons why functions
 *		must treat their input arguments as read-only.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalConst(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    Const* con = NULL;
    if (IsA(exprstate->expr, UserVar)) {
        bool found = false;
        UserVar *uservar = (UserVar *)exprstate->expr;
        GucUserParamsEntry *entry = (GucUserParamsEntry *)hash_search(u_sess->utils_cxt.set_user_params_htab, uservar->name, HASH_FIND, &found);

        /* if not found, return a null const */
        if (found) {
            if (entry->isParse) {
                con = (Const *)uservar->value;
            } else {
                Node *node = coerce_type(NULL, (Node *)entry->value, entry->value->consttype, ((Const *)uservar->value)->consttype,
                    -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1);
                node = eval_const_expression_value(NULL, node, NULL);
                if (nodeTag(node) != T_Const) {
                    ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION),
                        errmsg("The value of a user_defined variable must be convertible to a constant.")));
                }
                con = (Const *)node;
            }
        } else {
            con = makeConst(UNKNOWNOID, -1, InvalidOid, -2, (Datum)0, true, false);
        }
    } else if (IsA(exprstate->expr, SetVariableExpr)) {
        SetVariableExpr* setvar = (SetVariableExpr*)transformSetVariableExpr((SetVariableExpr*)exprstate->expr);
        con = (Const*)setvar->value;
    } else {
        con = (Const*)exprstate->expr;
    }

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    *isNull = con->constisnull;

    /* if a const cursor, copy cursor option data to econtext */
    if (econtext->is_cursor && con->consttype == REFCURSOROID) {
        CopyCursorInfoData(&econtext->cursor_data, &con->cursor_data);
        econtext->dno = con->cursor_data.cur_dno;
    }

    return con->constvalue;
}

/* ----------------------------------------------------------------
 * ExecEvalRownum: Returns the rownum
 * ----------------------------------------------------------------
 */
static Datum ExecEvalRownum(RownumState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    if (isDone != NULL)
        *isDone = ExprSingleResult;
    *isNull = false;

    if (ROWNUM_TYPE_COMPAT) {
        return DirectFunctionCall1(int8_numeric, Int64GetDatum(exprstate->ps->ps_rownum + 1));
    } else {
        return Int64GetDatum(exprstate->ps->ps_rownum + 1);
    }
}

/* ----------------------------------------------------------------
 *		ExecEvalParamExec
 *
 *		Returns the value of a PARAM_EXEC parameter.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalParamExec(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    Param* expression = (Param*)exprstate->expr;
    int thisParamId = expression->paramid;
    ParamExecData* prm = NULL;

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    /*
     * PARAM_EXEC params (internal executor parameters) are stored in the
     * ecxt_param_exec_vals array, and can be accessed by array index.
     */
    prm = &(econtext->ecxt_param_exec_vals[thisParamId]);
    if (prm->execPlan != NULL) {
        /* Parameter not evaluated yet, so go do it */
        ExecSetParamPlan((SubPlanState*)prm->execPlan, econtext);
        /* ExecSetParamPlan should have processed this param... */
        Assert(prm->execPlan == NULL);
        prm->isConst = true;
        prm->valueType = expression->paramtype;
    }
    *isNull = prm->isnull;
    prm->isChanged = true;
    return prm->value;
}

/* ----------------------------------------------------------------
 *		ExecEvalParamExtern
 *
 *		Returns the value of a PARAM_EXTERN parameter.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalParamExtern(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    Param* expression = (Param*)exprstate->expr;
    int thisParamId = expression->paramid;
    ParamListInfo paramInfo = econtext->ecxt_param_list_info;

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    /*
     * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
     */
    if (paramInfo && thisParamId > 0 && thisParamId <= paramInfo->numParams) {
        ParamExternData* prm = &paramInfo->params[thisParamId - 1];

        /* give hook a chance in case parameter is dynamic */
        if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
            (*paramInfo->paramFetch)(paramInfo, thisParamId);

        if (OidIsValid(prm->ptype)) {
            /* safety check in case hook did something unexpected */
            if (prm->ptype != expression->paramtype)
                ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                        errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
                            thisParamId,
                            format_type_be(prm->ptype),
                            format_type_be(expression->paramtype))));

            *isNull = prm->isnull;
            if (prm->tabInfo && prm->tabInfo->isnestedtable && plpgsql_estate) {
                plpgsql_estate->curr_nested_table_type = prm->ptype;
                plpgsql_estate->curr_nested_table_layers = prm->tabInfo->tableOfLayers;
            }
            /* copy cursor option from param to econtext */
            if (econtext->is_cursor && prm->ptype == REFCURSOROID) {
                CopyCursorInfoData(&econtext->cursor_data, &prm->cursor_data);
                econtext->dno = thisParamId - 1;
            }
            return prm->value;
        }
    }

    ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("no value found for parameter %d", thisParamId)));
    return (Datum)0; /* keep compiler quiet */
}

void initExecTableOfIndexInfo(ExecTableOfIndexInfo* execTableOfIndexInfo, ExprContext* econtext)
{
    execTableOfIndexInfo->econtext = econtext;
    execTableOfIndexInfo->tableOfIndex = NULL;
    execTableOfIndexInfo->tableOfIndexType = InvalidOid;
    execTableOfIndexInfo->isnestedtable = false;
    execTableOfIndexInfo->tableOfLayers = 0;
    execTableOfIndexInfo->paramid = -1;
    execTableOfIndexInfo->paramtype = InvalidOid;
}

/* this function is only used for getting table of index inout param */
static bool get_tableofindex_param(Node* node, ExecTableOfIndexInfo* execTableOfIndexInfo)
{
    if (node == NULL)
        return false;
    if (IsA(node, Param)) {
        execTableOfIndexInfo->paramid = ((Param*)node)->paramid;
        execTableOfIndexInfo->paramtype = ((Param*)node)->paramtype;
        return true;
    }
    return false;
}

static bool IsTableOfFunc(Oid funcOid)
{
    const Oid array_function_start_oid = 7881;
    const Oid array_function_end_oid = 7892;
    const Oid array_indexby_delete_oid = 7896;
    return (funcOid >= array_function_start_oid && funcOid <= array_function_end_oid) ||
        funcOid == array_indexby_delete_oid;
}

/* ----------------------------------------------------------------
 *		ExecEvalParamExternTableOfIndex
 *
 *		Returns the value of a PARAM_EXTERN table of index and type parameter .
 * ----------------------------------------------------------------
 */
void ExecEvalParamExternTableOfIndex(Node* node, ExecTableOfIndexInfo* execTableOfIndexInfo)
{
    if (get_tableofindex_param(node, execTableOfIndexInfo)) {
        ExecEvalParamExternTableOfIndexById(execTableOfIndexInfo);
    }
}

bool ExecEvalParamExternTableOfIndexById(ExecTableOfIndexInfo* execTableOfIndexInfo)
{
    if (execTableOfIndexInfo->paramid == -1) {
        return false;
    }

    int thisParamId = execTableOfIndexInfo->paramid;
    ParamListInfo paramInfo = execTableOfIndexInfo->econtext->ecxt_param_list_info;

    /*
     * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
     */
    if (paramInfo && thisParamId > 0 && thisParamId <= paramInfo->numParams) {
        ParamExternData* prm = &paramInfo->params[thisParamId - 1];

        /* give hook a chance in case parameter is dynamic */
        if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
            (*paramInfo->paramFetch)(paramInfo, thisParamId);

        if (OidIsValid(prm->ptype) && prm->tabInfo != NULL &&
            prm->tabInfo->tableOfIndex != NULL && OidIsValid(prm->tabInfo->tableOfIndexType)) {
            /* safety check in case hook did something unexpected */
            if (prm->ptype != execTableOfIndexInfo->paramtype)
                ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                        errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
                            thisParamId,
                            format_type_be(prm->ptype),
                            format_type_be(execTableOfIndexInfo->paramtype))));
            execTableOfIndexInfo->tableOfIndexType = prm->tabInfo->tableOfIndexType;
            execTableOfIndexInfo->isnestedtable = prm->tabInfo->isnestedtable;
            execTableOfIndexInfo->tableOfLayers = prm->tabInfo->tableOfLayers;
            execTableOfIndexInfo->tableOfIndex = prm->tabInfo->tableOfIndex;
            return true;
        }
    }
    return false;
}


/* ----------------------------------------------------------------
 *		ExecEvalOper / ExecEvalFunc support routines
 * ----------------------------------------------------------------
 */
/*
 *		GetAttributeByName
 *		GetAttributeByNum
 *
 *		These functions return the value of the requested attribute
 *		out of the given tuple Datum.
 *		C functions which take a tuple as an argument are expected
 *		to use these.  Ex: overpaid(EMP) might call GetAttributeByNum().
 *		Note: these are actually rather slow because they do a typcache
 *		lookup on each call.
 */
Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno, bool* isNull)
{
    Datum result;
    Oid tupType;
    int32 tupTypmod;
    TupleDesc tupDesc;
    HeapTupleData tmptup;

    if (!AttributeNumberIsValid(attrno))
        ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid attribute number %d", attrno)));

    if (isNull == NULL)
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                errmsg("a NULL isNull pointer was passed when get attribute by number.")));

    if (tuple == NULL) {
        /* Kinda bogus but compatible with old behavior... */
        *isNull = true;
        return (Datum)0;
    }

    tupType = HeapTupleHeaderGetTypeId(tuple);
    tupTypmod = HeapTupleHeaderGetTypMod(tuple);
    tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);

    /*
     * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set all
     * the fields in the struct just in case user tries to inspect system
     * columns.
     */
    tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
    ItemPointerSetInvalid(&(tmptup.t_self));
    tmptup.t_tableOid = InvalidOid;
    tmptup.t_bucketId = InvalidBktId;
    HeapTupleSetZeroBase(&tmptup);
#ifdef PGXC
    tmptup.t_xc_node_id = 0;
#endif
    tmptup.t_data = tuple;

    if (attrno == -3 || attrno == -5) {
        elog(WARNING, "system attribute xmin or xmax,  the results about this attribute are untrustworthy.");
    }
    result = tableam_tops_tuple_getattr(&tmptup, attrno, tupDesc, isNull);

    ReleaseTupleDesc(tupDesc);

    return result;
}

Datum GetAttributeByName(HeapTupleHeader tuple, const char* attname, bool* isNull)
{
    AttrNumber attrno;
    Datum result;
    Oid tupType;
    int32 tupTypmod;
    TupleDesc tupDesc;
    HeapTupleData tmptup;
    int i;

    if (attname == NULL)
        ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid null attribute name")));

    if (isNull == NULL)
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                errmsg("a NULL isNull pointer was passed when get attribute by name.")));

    if (tuple == NULL) {
        /* Kinda bogus but compatible with old behavior... */
        *isNull = true;
        return (Datum)0;
    }

    tupType = HeapTupleHeaderGetTypeId(tuple);
    tupTypmod = HeapTupleHeaderGetTypMod(tuple);
    tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);

    attrno = InvalidAttrNumber;
    for (i = 0; i < tupDesc->natts; i++) {
        if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0) {
            attrno = tupDesc->attrs[i]->attnum;
            break;
        }
    }

    if (attrno == InvalidAttrNumber)
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_ATTRIBUTE),
                errmodule(MOD_EXECUTOR),
                errmsg("attribute \"%s\" does not exist", attname)));

    /*
     * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set all
     * the fields in the struct just in case user tries to inspect system
     * columns.
     */
    tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
    ItemPointerSetInvalid(&(tmptup.t_self));
    tmptup.t_tableOid = InvalidOid;
    tmptup.t_bucketId = InvalidBktId;
    HeapTupleSetZeroBase(&tmptup);
#ifdef PGXC
    tmptup.t_xc_node_id = 0;
#endif
    tmptup.t_data = tuple;

    if (attrno == -3 || attrno == -5) {
        elog(WARNING, "system attribute \"%s\",  the results about this attribute are untrustworthy.", attname);
    }
    result = tableam_tops_tuple_getattr(&tmptup, attrno, tupDesc, isNull);

    ReleaseTupleDesc(tupDesc);

    return result;
}

/*
 * Find the real function return type based on the actual func args' types.
 * @inPara arg_num: the number of func's args.
 * @inPara actual_arg_types: the type array of actual func args'.
 * @inPara fcache: the FuncExprState of this functin.
 * @return Oid: the real func return type.
 */
static Oid getRealFuncRetype(int arg_num, Oid* actual_arg_types, FuncExprState* fcache)
{
    Oid funcid = fcache->func.fn_oid;
    Oid rettype = fcache->func.fn_rettype;

    /* Find the declared arg types in PROCOID by funcid. */
    HeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
    if (!HeapTupleIsValid(proctup))
        ereport(ERROR,
            (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                errmodule(MOD_EXECUTOR),
                errmsg("cache lookup failed for function %u", funcid)));

    oidvector* proargs = ProcedureGetArgTypes(proctup);
    Oid* declared_arg_types = proargs->values;

    /* Find the real return type based on the declared arg types and actual arg types.*/
    rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, arg_num, rettype, false);

    ReleaseSysCache(proctup);
    return rettype;
}

/*
 * Check whether the function is a set function supported by the vector engine.
 */
static bool isVectorEngineSupportSetFunc(Oid funcid)
{
    switch (funcid) {
        case OID_REGEXP_SPLIT_TO_TABLE:                // regexp_split_to_table
        case OID_REGEXP_SPLIT_TO_TABLE_NO_FLAG:        // regexp_split_to_table
        case OID_ARRAY_UNNEST:                         // unnest
            return true;
            break;
        default:
            return false;
            break;
    }
}

/*
 * init_fcache - initialize a FuncExprState node during first use
 */
template <bool vectorized>
static void init_fcache(
    Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt, bool needDescForSets)
{
    AclResult aclresult;
    MemoryContext oldcontext;
    Form_pg_proc procStruct;
    HeapTuple procTup;
    Oid definer = GetUserId();
    
    if (u_sess->attr.attr_sql.sql_compatibility ==  B_FORMAT)
    {
        /* Get the function's pg_proc entry */
        procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(foid));
        if (!HeapTupleIsValid(procTup)) {
            ereport(ERROR, (errmodule(MOD_EXECUTOR), errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                    errmsg("cache lookup failed for function %u", foid)));
        }
        procStruct = (Form_pg_proc)GETSTRUCT(procTup);
        if (procStruct->prosecdef)
            definer = procStruct->proowner;
        ReleaseSysCache(procTup);
    }

    /* Check permission to call function */
    aclresult = pg_proc_aclcheck(foid, definer, ACL_EXECUTE);
    if (aclresult != ACLCHECK_OK)
        aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid));

    /*
     * Safety check on nargs.  Under normal circumstances this should never
     * fail, as parser should check sooner.  But possibly it might fail if
     * server has been compiled with FUNC_MAX_ARGS smaller than some functions
     * declared in pg_proc?
     */
    if (list_length(fcache->args) > FUNC_MAX_ARGS)
        ereport(ERROR,
            (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
                errmsg_plural("cannot pass more than %d argument to a function",
                    "cannot pass more than %d arguments to a function",
                    FUNC_MAX_ARGS,
                    FUNC_MAX_ARGS)));

    /* Set up the primary fmgr lookup information */
    fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
    fmgr_info_set_expr((Node*)fcache->xprstate.expr, &(fcache->func));

    /* palloc args in fcache's context  */
    oldcontext = MemoryContextSwitchTo(fcacheCxt);
    /* Initialize the function call parameter struct as well */
    if (vectorized)
        InitVecFunctionCallInfoData(
            &fcache->fcinfo_data, &(fcache->func), list_length(fcache->args), input_collation, NULL, NULL);
    else
        InitFunctionCallInfoData(
            fcache->fcinfo_data, &(fcache->func), list_length(fcache->args), input_collation, NULL, NULL);

    if (vectorized) {
        int nargs = list_length(fcache->args);
        ListCell* cell = NULL;
        GenericFunRuntime* genericRuntime = NULL;
        errno_t rc;

        if (fcache->fcinfo_data.flinfo->genericRuntime == NULL) {
            genericRuntime = (GenericFunRuntime*)palloc0(sizeof(GenericFunRuntime));
            InitGenericFunRuntimeInfo(*genericRuntime, nargs);
            fcache->fcinfo_data.flinfo->genericRuntime = genericRuntime;
        } else {
            genericRuntime = fcache->fcinfo_data.flinfo->genericRuntime;

            /* if internalFinfo is not null, release the internalFinfo's memory and set the pointer to null */
            if (genericRuntime->internalFinfo != NULL) {
                FreeFunctionCallInfoData(*(genericRuntime->internalFinfo));
                genericRuntime->internalFinfo = NULL;
            }

            /* reset the memory for reuse */
            rc = memset_s(genericRuntime->args,
                sizeof(GenericFunRuntimeArg) * genericRuntime->compacity,
                0,
                sizeof(GenericFunRuntimeArg) * genericRuntime->compacity);
            securec_check(rc, "\0", "\0");

            rc = memset_s(genericRuntime->inputargs,
                sizeof(Datum) * genericRuntime->compacity,
                0,
                sizeof(Datum) * genericRuntime->compacity);
            securec_check(rc, "\0", "\0");

            rc = memset_s(genericRuntime->nulls,
                sizeof(bool) * genericRuntime->compacity,
                0,
                sizeof(bool) * genericRuntime->compacity);
            securec_check(rc, "\0", "\0");

            /* we have to adjust the GenericFunRuntimeArg when
             * a) nargs is larger than genericRuntime->compacity, which means the allocated memory is not enough to hold
             *    all the argumnets here, we should enlarge the memory.
             * b) nargs is less than VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS while the allocated memory is much more
             * than that. As VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS is already enough in most senerios, we should
             * reduce the memory.
             *
             * NOTE: To avoid memory wasting and memory fragments, we free and initilized a new GenericFunRuntimeArg.
             */
            if (unlikely(nargs > genericRuntime->compacity) ||
                (unlikely(genericRuntime->compacity > VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS) &&
                    nargs <= VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS)) {
                FreeGenericFunRuntimeInfo(*genericRuntime);
                InitGenericFunRuntimeInfo(*genericRuntime, nargs);
            }
        }

        ScalarVector* pVector = New(CurrentMemoryContext) ScalarVector[nargs];

        int i = 0;
        if (fcache->args && fcache->args->length > 0) {
            Oid* actual_arg_types = (Oid*)palloc0(fcache->args->length * sizeof(Oid));

            foreach (cell, fcache->args) {
                ExprState* argstate = (ExprState*)lfirst(cell);
                Oid funcrettype;
                TupleDesc tupdesc;
                ScalarDesc desc;

                (void)get_expr_result_type((Node*)argstate->expr, &funcrettype, &tupdesc);

                desc.typeId = funcrettype;
                desc.encoded = COL_IS_ENCODE(funcrettype);
                fcache->fcinfo_data.flinfo->genericRuntime->args[i].argType = funcrettype;

                pVector[i].init(CurrentMemoryContext, desc);
                /* Record the real arg types from sub functions. */
                actual_arg_types[i] = funcrettype;
                i++;
            }

            /* Find the real return type for func with return type like ANYELEMENT. */
            fcache->fcinfo_data.flinfo->fn_rettype = getRealFuncRetype(i, actual_arg_types, fcache);
            pfree_ext(actual_arg_types);
        }
        fcache->fcinfo_data.argVector = pVector;
    }
    (void)MemoryContextSwitchTo(oldcontext);

    if (vectorized) {
        if (fcache->func.fn_retset == true) {
            if (!isVectorEngineSupportSetFunc(fcache->func.fn_oid)) {
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmodule(MOD_EXECUTOR),
                        errmsg("set-return function not supported in vector eninge")));
            }
        }
        fcache->funcResultDesc = NULL;
    } else {
        /* If function returns set, prepare expected tuple descriptor */
        if (fcache->func.fn_retset && needDescForSets) {
            TypeFuncClass functypclass;
            Oid funcrettype;
            TupleDesc tupdesc;
            MemoryContext oldmemcontext;

            functypclass = get_expr_result_type(fcache->func.fn_expr, &funcrettype, &tupdesc);

            /* Must save tupdesc in fcache's context */
            oldmemcontext = MemoryContextSwitchTo(fcacheCxt);

            if (functypclass == TYPEFUNC_COMPOSITE) {
                /* Composite data type, e.g. a table's row type */
                Assert(tupdesc);
                /* Must copy it out of typcache for safety */
                fcache->funcResultDesc = CreateTupleDescCopy(tupdesc);
                fcache->funcReturnsTuple = true;
            } else if (functypclass == TYPEFUNC_SCALAR) {
                /* Base data type, i.e. scalar */
                tupdesc = CreateTemplateTupleDesc(1, false, TAM_HEAP);
                TupleDescInitEntry(tupdesc, (AttrNumber)1, NULL, funcrettype, -1, 0);
                fcache->funcResultDesc = tupdesc;
                fcache->funcReturnsTuple = false;
            } else if (functypclass == TYPEFUNC_RECORD) {
                /* This will work if function doesn't need an expectedDesc */
                fcache->funcResultDesc = NULL;
                fcache->funcReturnsTuple = true;
            } else {
                /* Else, we will fail if function needs an expectedDesc */
                fcache->funcResultDesc = NULL;
            }

            MemoryContextSwitchTo(oldmemcontext);
        } else
            fcache->funcResultDesc = NULL;
    }

    /* Initialize additional state */
    fcache->funcResultStore = NULL;
    fcache->funcResultSlot = NULL;
    fcache->setArgsValid = false;
    fcache->shutdown_reg = false;
}

void initVectorFcache(Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt)
{
    init_fcache<true>(foid, input_collation, fcache, fcacheCxt, false);
}

/*
 * callback function in case a FuncExpr returning a set needs to be shut down
 * before it has been run to completion
 */
static void ShutdownFuncExpr(Datum arg)
{
    FuncExprState* fcache = (FuncExprState*)DatumGetPointer(arg);

    /* If we have a slot, make sure it's let go of any tuplestore pointer */
    if (fcache->funcResultSlot)
        (void)ExecClearTuple(fcache->funcResultSlot);

    /* Release any open tuplestore */
    if (fcache->funcResultStore)
        tuplestore_end(fcache->funcResultStore);
    fcache->funcResultStore = NULL;

    /* Clear any active set-argument state */
    fcache->setArgsValid = false;

    /* execUtils will deregister the callback... */
    fcache->shutdown_reg = false;
}

/*
 * get_cached_rowtype: utility function to lookup a rowtype tupdesc
 *
 * type_id, typmod: identity of the rowtype
 * cache_field: where to cache the TupleDesc pointer in expression state node
 *		(field must be initialized to NULL)
 * econtext: expression context we are executing in
 *
 * NOTE: because the shutdown callback will be called during plan rescan,
 * must be prepared to re-do this during any node execution; cannot call
 * just once during expression initialization
 */
static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod, TupleDesc* cache_field, ExprContext* econtext)
{
    TupleDesc tupDesc = *cache_field;

    /* Do lookup if no cached value or if requested type changed */
    if (tupDesc == NULL || type_id != tupDesc->tdtypeid || typmod != tupDesc->tdtypmod) {
        tupDesc = lookup_rowtype_tupdesc(type_id, typmod);

        if (*cache_field) {
            /* Release old tupdesc; but callback is already registered */
            ReleaseTupleDesc(*cache_field);
        } else {
            /* Need to register shutdown callback to release tupdesc */
            RegisterExprContextCallback(econtext, ShutdownTupleDescRef, PointerGetDatum(cache_field));
        }
        *cache_field = tupDesc;
    }
    return tupDesc;
}

/*
 * Callback function to release a tupdesc refcount at expression tree shutdown
 */
static void ShutdownTupleDescRef(Datum arg)
{
    TupleDesc* cache_field = (TupleDesc*)DatumGetPointer(arg);

    if (*cache_field)
        ReleaseTupleDesc(*cache_field);
    *cache_field = NULL;
}

/*
 * Evaluate arguments for a function.
 */
template <bool has_refcursor>
static ExprDoneCond ExecEvalFuncArgs(
    FunctionCallInfo fcinfo, List* argList, ExprContext* econtext, int* plpgsql_var_dno)
{
    ExprDoneCond argIsDone;
    int i;
    ListCell* arg = NULL;

    argIsDone = ExprSingleResult; /* default assumption */

    i = 0;
    econtext->is_cursor = false;
    u_sess->plsql_cxt.func_tableof_index = NIL;
    bool is_have_huge_clob = false;
    foreach (arg, argList) {
        ExprState* argstate = (ExprState*)lfirst(arg);
        ExprDoneCond thisArgIsDone;

        if (has_refcursor && argstate->resultType == REFCURSOROID)
            econtext->is_cursor = true;
        fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo->argnull[i], &thisArgIsDone);
        ExecTableOfIndexInfo execTableOfIndexInfo;
        initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext);
        ExecEvalParamExternTableOfIndex((Node*)argstate->expr, &execTableOfIndexInfo);
        if (execTableOfIndexInfo.tableOfIndex != NULL) {
            MemoryContext oldCxt = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER));
            PLpgSQL_func_tableof_index* func_tableof =
                (PLpgSQL_func_tableof_index*)palloc0(sizeof(PLpgSQL_func_tableof_index));
            func_tableof->varno = i;
            func_tableof->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType;
            func_tableof->tableOfIndex = copyTableOfIndex(execTableOfIndexInfo.tableOfIndex);
            u_sess->plsql_cxt.func_tableof_index = lappend(u_sess->plsql_cxt.func_tableof_index, func_tableof);
            MemoryContextSwitchTo(oldCxt);
        }

        if (has_refcursor && econtext->is_cursor && plpgsql_var_dno != NULL) {
            plpgsql_var_dno[i] = econtext->dno;
            CopyCursorInfoData(&fcinfo->refcursor_data.argCursor[i], &econtext->cursor_data);
        }
        fcinfo->argTypes[i] = argstate->resultType;
        econtext->is_cursor = false;
        if (is_huge_clob(fcinfo->argTypes[i], fcinfo->argnull[i], fcinfo->arg[i])) {
            is_have_huge_clob = true;
        }

        if (thisArgIsDone != ExprSingleResult) {
            /*
             * We allow only one argument to have a set value; we'd need much
             * more complexity to keep track of multiple set arguments (cf.
             * ExecTargetList) and it doesn't seem worth it.
             */
            if (argIsDone != ExprSingleResult)
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("functions and operators can take at most one set argument")));
            argIsDone = thisArgIsDone;
        }
        i++;
    }
    check_huge_clob_paramter(fcinfo, is_have_huge_clob);

    Assert(i == fcinfo->nargs);

    return argIsDone;
}

/*
 *		ExecPrepareTuplestoreResult
 *
 * Subroutine for ExecMakeFunctionResult: prepare to extract rows from a
 * tuplestore function result.	We must set up a funcResultSlot (unless
 * already done in a previous call cycle) and verify that the function
 * returned the expected tuple descriptor.
 */
static void ExecPrepareTuplestoreResult(
    FuncExprState* fcache, ExprContext* econtext, Tuplestorestate* resultStore, TupleDesc resultDesc)
{
    fcache->funcResultStore = resultStore;

    if (fcache->funcResultSlot == NULL) {
        /* Create a slot so we can read data out of the tuplestore */
        TupleDesc slotDesc;
        MemoryContext oldcontext;

        oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt);

        /*
         * If we were not able to determine the result rowtype from context,
         * and the function didn't return a tupdesc, we have to fail.
         */
        if (fcache->funcResultDesc)
            slotDesc = fcache->funcResultDesc;
        else if (resultDesc) {
            /* don't assume resultDesc is long-lived */
            slotDesc = CreateTupleDescCopy(resultDesc);
        } else {
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("function returning setof record called in context that cannot accept type record")));
            slotDesc = NULL; /* keep compiler quiet */
        }

        fcache->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
        MemoryContextSwitchTo(oldcontext);
    }

    /*
     * If function provided a tupdesc, cross-check it.	We only really need to
     * do this for functions returning RECORD, but might as well do it always.
     */
    if (resultDesc) {
        if (fcache->funcResultDesc)
            tupledesc_match(fcache->funcResultDesc, resultDesc);

        /*
         * If it is a dynamically-allocated TupleDesc, free it: it is
         * typically allocated in a per-query context, so we must avoid
         * leaking it across multiple usages.
         */
        if (resultDesc->tdrefcount == -1)
            FreeTupleDesc(resultDesc);
    }

    /* Register cleanup callback if we didn't already */
    if (!fcache->shutdown_reg) {
        RegisterExprContextCallback(econtext, ShutdownFuncExpr, PointerGetDatum(fcache));
        fcache->shutdown_reg = true;
    }
}

/*
 * Check that function result tuple type (src_tupdesc) matches or can
 * be considered to match what the query expects (dst_tupdesc). If
 * they don't match, ereport.
 *
 * We really only care about number of attributes and data type.
 * Also, we can ignore type mismatch on columns that are dropped in the
 * destination type, so long as the physical storage matches.  This is
 * helpful in some cases involving out-of-date cached plans.
 */
static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
{
    int i;

    if (dst_tupdesc->natts != src_tupdesc->natts)
        ereport(ERROR,
            (errcode(ERRCODE_DATATYPE_MISMATCH),
                errmsg("function return row and query-specified return row do not match"),
                errdetail_plural("Returned row contains %d attribute, but query expects %d.",
                    "Returned row contains %d attributes, but query expects %d.",
                    src_tupdesc->natts,
                    src_tupdesc->natts,
                    dst_tupdesc->natts)));

    for (i = 0; i < dst_tupdesc->natts; i++) {
        Form_pg_attribute dattr = dst_tupdesc->attrs[i];
        Form_pg_attribute sattr = src_tupdesc->attrs[i];

        if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid))
            continue; /* no worries */
        if (!dattr->attisdropped)
            ereport(ERROR,
                (errcode(ERRCODE_DATATYPE_MISMATCH),
                    errmsg("function return row and query-specified return row do not match"),
                    errdetail("Returned type %s at ordinal position %d, but query expects %s.",
                        format_type_be(sattr->atttypid),
                        i + 1,
                        format_type_be(dattr->atttypid))));

        if (dattr->attlen != sattr->attlen || dattr->attalign != sattr->attalign)
            ereport(ERROR,
                (errcode(ERRCODE_DATATYPE_MISMATCH),
                    errmsg("function return row and query-specified return row do not match"),
                    errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.", i + 1)));
    }
}

static void set_result_for_plpgsql_language_function_with_outparam(FuncExprState *fcache, Datum *result, bool *isNull)
{
    if (!IsA(fcache->xprstate.expr, FuncExpr)) {
        return;
    }
    FuncExpr *func = (FuncExpr *)fcache->xprstate.expr;
    if (!is_function_with_plpgsql_language_and_outparam(func->funcid)) {
        return;
    }
    HeapTupleHeader td = DatumGetHeapTupleHeader(*result);
    TupleDesc tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td));
    HeapTupleData tup;
    tup.t_len = HeapTupleHeaderGetDatumLength(td);
    tup.t_data = td;
    Datum *values = (Datum *)palloc(sizeof(Datum) * tupdesc->natts);
    bool *nulls = (bool *)palloc(sizeof(bool) * tupdesc->natts);
    heap_deform_tuple(&tup, tupdesc, values, nulls);
    *result = values[0];
    *isNull = nulls[0];
    pfree(values);
    pfree(nulls);
}

/*
 *		ExecMakeFunctionResult
 *
 * Evaluate the arguments to a function and then the function itself.
 * init_fcache is presumed already run on the FuncExprState.
 *
 * This function handles the most general case, wherein the function or
 * one of its arguments can return a set.
 *
 * Note: This function use template parameter can compile different function,
 * reduce the assembly instructions so as to improve performance.
 *
 * Template parameter:
 * @bool has_cursor_return - need store out-args cursor info.
 * @bool has_refcursor - need store in-args cursor info.
 * @bool isSetReturnFunc - indicate function returns a set.
 */
template <bool has_refcursor, bool has_cursor_return, bool isSetReturnFunc>
static Datum ExecMakeFunctionResult(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    List* arguments = NIL;
    Datum result;
    FunctionCallInfo fcinfo;
    PgStat_FunctionCallUsage fcusage;
    ReturnSetInfo rsinfo; /* for functions returning sets */
    ExprDoneCond argDone;
    bool hasSetArg = false;
    int i;
    int* var_dno = NULL;

    econtext->plpgsql_estate = plpgsql_estate;
    plpgsql_estate = NULL;

restart:

    /* Guard against stack overflow due to overly complex expressions */
    check_stack_depth();

    /*
     * If a previous call of the function returned a set result in the form of
     * a tuplestore, continue reading rows from the tuplestore until it's
     * empty.
     */
    if (fcache->funcResultStore) {
        /* it was provided before ... */
        if (unlikely(isDone == NULL)) {
            ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                    errmsg("set-valued function called in context that cannot accept a set")));
        }
        econtext->hasSetResultStore = true;
        if (tuplestore_gettupleslot(fcache->funcResultStore, true, false, fcache->funcResultSlot)) {
            *isDone = ExprMultipleResult;
            if (fcache->funcReturnsTuple) {
                /* We must return the whole tuple as a Datum. */
                *isNull = false;
                return ExecFetchSlotTupleDatum(fcache->funcResultSlot);
            } else {
                /* Extract the first column and return it as a scalar. */
                Assert(fcache->funcResultSlot != NULL);
                /* Get the Table Accessor Method*/
                return tableam_tslot_getattr(fcache->funcResultSlot, 1, isNull);
            }
        }
        /* Exhausted the tuplestore, so clean up */
        tuplestore_end(fcache->funcResultStore);
        fcache->funcResultStore = NULL;
        /* We are done unless there was a set-valued argument */
        if (!fcache->setHasSetArg) {
            *isDone = ExprEndResult;
            *isNull = true;
            return (Datum)0;
        }
        /* If there was, continue evaluating the argument values */
        Assert(!fcache->setArgsValid);
    }

    /*
     * arguments is a list of expressions to evaluate before passing to the
     * function manager.  We skip the evaluation if it was already done in the
     * previous call (ie, we are continuing the evaluation of a set-valued
     * function).  Otherwise, collect the current argument values into fcinfo.
     */
    fcinfo = &fcache->fcinfo_data;

    if (has_cursor_return) {
        /* init returnCursor to store out-args cursor info on ExprContext*/
        fcinfo->refcursor_data.returnCursor =
            (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->refcursor_data.return_number);
    } else {
        fcinfo->refcursor_data.returnCursor = NULL;
    }

    if (has_refcursor) {
        /* init argCursor to store in-args cursor info on ExprContext*/
        fcinfo->refcursor_data.argCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->nargs);
        var_dno = (int*)palloc0(sizeof(int) * fcinfo->nargs);
        for (i = 0; i < fcinfo->nargs; i++) {
            var_dno[i] = -1;
        }
    }

    arguments = fcache->args;
    if (!fcache->setArgsValid) {
        if (has_refcursor)
            argDone = ExecEvalFuncArgs<true>(fcinfo, arguments, econtext, var_dno);
        else
            argDone = ExecEvalFuncArgs<false>(fcinfo, arguments, econtext);
        if (argDone == ExprEndResult) {
            /* input is an empty set, so return an empty set. */
            *isNull = true;
            if (isDone != NULL)
                *isDone = ExprEndResult;
            else
                ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("set-valued function called in context that cannot accept a set")));
            return (Datum)0;
        }
        hasSetArg = (argDone != ExprSingleResult);
    } else {
        /* Re-use callinfo from previous evaluation */
        hasSetArg = fcache->setHasSetArg;
        /* Reset flag (we may set it again below) */
        fcache->setArgsValid = false;
    }

    /*
     * Now call the function, passing the evaluated parameter values.
     */
    if (fcache->func.fn_retset || hasSetArg) {
        /*
         * We need to return a set result.	Complain if caller not ready to
         * accept one.
         */
        if (isDone == NULL)
            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("set-valued function called in context that cannot accept a set")));

        /*
         * Prepare a resultinfo node for communication.  If the function
         * doesn't itself return set, we don't pass the resultinfo to the
         * function, but we need to fill it in anyway for internal use.
         */
        if (fcache->func.fn_retset)
            fcinfo->resultinfo = (Node*)&rsinfo;
        rsinfo.type = T_ReturnSetInfo;
        rsinfo.econtext = econtext;
        rsinfo.expectedDesc = fcache->funcResultDesc;
        rsinfo.allowedModes = (int)(SFRM_ValuePerCall | SFRM_Materialize);
        /* note we do not set SFRM_Materialize_Random or _Preferred */
        rsinfo.returnMode = SFRM_ValuePerCall;
        /* isDone is filled below */
        rsinfo.setResult = NULL;
        rsinfo.setDesc = NULL;

        /*
         * This loop handles the situation where we have both a set argument
         * and a set-valued function.  Once we have exhausted the function's
         * value(s) for a particular argument value, we have to get the next
         * argument value and start the function over again. We might have to
         * do it more than once, if the function produces an empty result set
         * for a particular input value.
         */
        for (;;) {
            /*
             * If function is strict, and there are any NULL arguments, skip
             * calling the function (at least for this set of args).
             */
            bool callit = true;

            if (fcache->func.fn_strict) {
                for (i = 0; i < fcinfo->nargs; i++) {
                    if (fcinfo->argnull[i]) {
                        callit = false;
                        break;
                    }
                }
            }

            if (callit) {
                pgstat_init_function_usage(fcinfo, &fcusage);

                fcinfo->isnull = false;
                rsinfo.isDone = ExprSingleResult;
                result = FunctionCallInvoke(fcinfo);
                *isNull = fcinfo->isnull;
                *isDone = rsinfo.isDone;

                pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult);
            } else if (isSetReturnFunc) {
                /*
                 * For a strict SRF, result for NULL is an empty set
                 * If SRF is strict and has any NULL arguments, this SRF
                 * need return empty set, so such rows were omitted entirely
                 * from the result set.
                 */
                result = (Datum)0;
                *isNull = true;
                *isDone = ExprEndResult;
            } else {
                /*
                 * For a strict non-SRF, result for NULL is a NULL.
                 * This branch in order to deal strict nested functions
                 * like "select plain_function(set_returning_function(...))".
                 * If some of the SRF outputs are NULL, and the plain function
                 * is strict, we expect to get NULL results for such rows
                 */
                result = (Datum)0;
                *isNull = true;
                *isDone = ExprSingleResult;
            }

            if (has_refcursor && econtext->plpgsql_estate != NULL) {
                PLpgSQL_execstate* estate = econtext->plpgsql_estate;
                /* copy in-args cursor option info */
                for (i = 0; i < fcinfo->nargs; i++) {
                    if (var_dno[i] >= 0) {
                        int dno = var_dno[i];
                        Cursor_Data* cursor_data = &fcinfo->refcursor_data.argCursor[i];
#ifdef USE_ASSERT_CHECKING
                        PLpgSQL_datum* datum = estate->datums[dno];
#endif
                        Assert(datum->dtype == PLPGSQL_DTYPE_VAR);
                        Assert(((PLpgSQL_var*)datum)->datatype->typoid == REFCURSOROID);

                        ExecCopyDataToDatum(estate->datums, dno, cursor_data);
                    }
                }

                if (fcinfo->refcursor_data.return_number > 0) {
                    /* copy function returns cursor option info.
                     * for simple expr in exec_eval_expr, we can not get the result type,
                     * so cursor_return_data mallocs here.
                     */
                    if (estate->cursor_return_data == NULL && estate->tuple_store_cxt != NULL) {
                        MemoryContext oldcontext = MemoryContextSwitchTo(estate->tuple_store_cxt);
                        estate->cursor_return_data =
                            (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->refcursor_data.return_number);
                        estate->cursor_return_numbers = fcinfo->refcursor_data.return_number;
                        (void)MemoryContextSwitchTo(oldcontext);
                    }

                    if (estate->cursor_return_data != NULL) {
                        for (i = 0; i < fcinfo->refcursor_data.return_number; i++) {
                            int rc = memcpy_s(&estate->cursor_return_data[i], sizeof(Cursor_Data),
                                &fcinfo->refcursor_data.returnCursor[i], sizeof(Cursor_Data));
                            securec_check(rc, "\0", "\0");
                        }
                    }
                }
            }

            /* Which protocol does function want to use? */
            if (rsinfo.returnMode == SFRM_ValuePerCall) {
                if (*isDone != ExprEndResult) {
                    /*
                     * Got a result from current argument. If function itself
                     * returns set, save the current argument values to re-use
                     * on the next call.
                     */
                    if (fcache->func.fn_retset && *isDone == ExprMultipleResult) {
                        fcache->setHasSetArg = hasSetArg;
                        fcache->setArgsValid = true;
                        /* Register cleanup callback if we didn't already */
                        if (!fcache->shutdown_reg) {
                            RegisterExprContextCallback(econtext, ShutdownFuncExpr, PointerGetDatum(fcache));
                            fcache->shutdown_reg = true;
                        }
                    }

                    /*
                     * Make sure we say we are returning a set, even if the
                     * function itself doesn't return sets.
                     */
                    if (hasSetArg) {
                        *isDone = ExprMultipleResult;
                    }
                    break;
                }
            } else if (rsinfo.returnMode == SFRM_Materialize) {
                /* check we're on the same page as the function author */
                if (rsinfo.isDone != ExprSingleResult)
                    ereport(ERROR,  (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
                            errmsg("table-function protocol for materialize mode was not followed")));
                if (rsinfo.setResult != NULL) {
                    /* prepare to return values from the tuplestore */
                    ExecPrepareTuplestoreResult(fcache, econtext, rsinfo.setResult, rsinfo.setDesc);
                    /* remember whether we had set arguments */
                    fcache->setHasSetArg = hasSetArg;
                    /* loop back to top to start returning from tuplestore */
                    goto restart;
                }
                /* if setResult was left null, treat it as empty set */
                *isDone = ExprEndResult;
                *isNull = true;
                result = (Datum)0;
            } else {
                ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
                        errmsg("unrecognized table-function returnMode: %d", (int)rsinfo.returnMode)));
            }

            /* Else, done with this argument */
            if (!hasSetArg) {
                break; /* input not a set, so done */
            }

            /* Re-eval args to get the next element of the input set */
            if (has_refcursor) {
                argDone = ExecEvalFuncArgs<true>(fcinfo, arguments, econtext, var_dno);
            } else {
                argDone = ExecEvalFuncArgs<false>(fcinfo, arguments, econtext);
            }

            if (argDone != ExprMultipleResult) {
                /* End of argument set, so we're done. */
                *isNull = true;
                *isDone = ExprEndResult;
                result = (Datum)0;
                break;
            }

            /*
             * If we reach here, loop around to run the function on the new
             * argument.
             */
        }
    } else {
        /*
         * Non-set case: much easier.
         *
         * In common cases, this code path is unreachable because we'd have
         * selected ExecMakeFunctionResultNoSets instead.  However, it's
         * possible to get here if an argument sometimes produces set results
         * and sometimes scalar results.  For example, a CASE expression might
         * call a set-returning function in only some of its arms.
         */
        if (isDone != NULL)
            *isDone = ExprSingleResult;

        /*
         * If function is strict, and there are any NULL arguments, skip
         * calling the function and return NULL.
         */
        if (fcache->func.fn_strict) {
            for (i = 0; i < fcinfo->nargs; i++) {
                if (fcinfo->argnull[i]) {
                    *isNull = true;
                    return (Datum)0;
                }
            }
        }

        pgstat_init_function_usage(fcinfo, &fcusage);

        fcinfo->isnull = false;
        result = FunctionCallInvoke(fcinfo);
        *isNull = fcinfo->isnull;

        pgstat_end_function_usage(&fcusage, true);
    }

    if (has_refcursor) {
        pfree_ext(fcinfo->refcursor_data.argCursor);
        pfree_ext(var_dno);
    }

    set_result_for_plpgsql_language_function_with_outparam(fcache, &result, isNull);

    return result;
}

/*
 *		ExecMakeFunctionResultNoSets
 *
 * Simplified version of ExecMakeFunctionResult that can only handle
 * non-set cases.  Hand-tuned for speed.
 *
 * Note: This function use template parameter can compile different function,
 * reduce the assembly instructions so as to improve performance.
 *
 * Template parameter:
 * @bool has_cursor_return - need store out-args cursor info.
 * @bool has_refcursor - need store in-args cursor info.
 */
template <bool has_refcursor, bool has_cursor_return>
static Datum ExecMakeFunctionResultNoSets(
    FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    ListCell* arg = NULL;
    Datum result;
    FunctionCallInfo fcinfo;
    PgStat_FunctionCallUsage fcusage;
    int i;
    int* var_dno = NULL;

    FunctionScanState *node = NULL;
    FuncExpr *fexpr = NULL;

    bool savedIsSTP = u_sess->SPI_cxt.is_stp;
    bool savedProConfigIsSet = u_sess->SPI_cxt.is_proconfig_set;
    bool proIsProcedure = false;
    bool supportTranaction = false;
    bool is_have_huge_clob = false;

#ifdef ENABLE_MULTIPLE_NODES
    if (IS_PGXC_COORDINATOR && (t_thrd.proc->workingVersionNum  >= STP_SUPPORT_COMMIT_ROLLBACK)) {
        supportTranaction = true;
    }
#else
    supportTranaction = true;
#endif
    bool needResetErrMsg = (u_sess->SPI_cxt.forbidden_commit_rollback_err_msg[0] == '\0');

    /* Only allow commit at CN, therefore only need to set atomic and
     * relevant check at CN level.
     */
    if (supportTranaction && IsA(fcache->xprstate.expr, FuncExpr)) {
        fexpr = (FuncExpr *) fcache->xprstate.expr;
        node = makeNode(FunctionScanState);
        if (!u_sess->SPI_cxt.is_allow_commit_rollback) {
            node->atomic = true;
        }
        else if (IsAfterTriggerBegin()) {
            node->atomic = true;
            stp_set_commit_rollback_err_msg(STP_XACT_AFTER_TRIGGER_BEGIN);
        }
        /*
         * If proconfig is set we can't allow transaction commands because of the
         * way the GUC stacking works: The transaction boundary would have to pop
         * the proconfig setting off the stack.  That restriction could be lifted
         * by redesigning the GUC nesting mechanism a bit.
         */
        if (!fcache->prokind) {
            bool isNullSTP = false;
            HeapTuple tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid));
            if (!HeapTupleIsValid(tp)) {
                ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),
                            errmsg("cache lookup failed for function %u", fexpr->funcid)));
            }
            if (!heap_attisnull(tp, Anum_pg_proc_proconfig, NULL) || u_sess->SPI_cxt.is_proconfig_set) {
                u_sess->SPI_cxt.is_proconfig_set = true;
                node->atomic = true;
                stp_set_commit_rollback_err_msg(STP_XACT_GUC_IN_OPT_CLAUSE);
            }
            /* immutable or stable function should not support commit/rollback */
            bool isNullVolatile = false;
            Datum provolatile = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_provolatile, &isNullVolatile);
            if (!isNullVolatile && CharGetDatum(provolatile) != PROVOLATILE_VOLATILE) {
                node->atomic = true;
                stp_set_commit_rollback_err_msg(STP_XACT_IMMUTABLE);
            }

            Datum datum = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_prokind, &isNullSTP);
            proIsProcedure = PROC_IS_PRO(CharGetDatum(datum));
            if (proIsProcedure) {
                fcache->prokind = 'p';
            } else {
                fcache->prokind = 'f';
            }

            /* if proIsProcedure is ture means it was a stored procedure */
            u_sess->SPI_cxt.is_stp = savedIsSTP;
            ReleaseSysCache(tp);
        } else {
            proIsProcedure = PROC_IS_PRO(fcache->prokind);
            u_sess->SPI_cxt.is_stp = savedIsSTP;
        }
    }

    /* Guard against stack overflow due to overly complex expressions */
    check_stack_depth();

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    econtext->plpgsql_estate = plpgsql_estate;
    plpgsql_estate = NULL;

    /* inlined, simplified version of ExecEvalFuncArgs */
    fcinfo = &fcache->fcinfo_data;

    /* init the number of arguments to a function*/
    InitFunctionCallInfoArgs(*fcinfo, list_length(fcache->args), 1);

    /* Only allow commit at CN, therefore need to set callcontext in CN only */
    if (supportTranaction) {
        fcinfo->context = (Node *)node;
    }

    if (econtext) {
        fcinfo->can_ignore = econtext->can_ignore;
    }

    /*
     * Incause of connet_by_root() and sys_connect_by_path() we need get the
     * current scan tuple slot so attach the econtext here
     *
     * NOTE: Have to revisit!! so I don't have better solution to handle the case
     *       where scantuple is available in built in funct
     */
    if (fcinfo->flinfo->fn_oid == CONNECT_BY_ROOT_FUNCOID ||
                fcinfo->flinfo->fn_oid == SYS_CONNECT_BY_PATH_FUNCOID) {
        fcinfo->swinfo.sw_econtext = (Node *)econtext;
        fcinfo->swinfo.sw_exprstate = (Node *)linitial(fcache->args);
    }

    if (has_cursor_return) {
        /* init returnCursor to store out-args cursor info on ExprContext*/
        fcinfo->refcursor_data.returnCursor =
            (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->refcursor_data.return_number);
    } else {
        fcinfo->refcursor_data.returnCursor = NULL;
    }

    if (has_refcursor) {
        /* init argCursor to store in-args cursor info on ExprContext */
        fcinfo->refcursor_data.argCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->nargs);
        var_dno = (int*)palloc0(sizeof(int) * fcinfo->nargs);
        for (i = 0; i < fcinfo->nargs; i++) {
            var_dno[i] = -1;
        }
    }

    i = 0;
    econtext->is_cursor = false;
    u_sess->plsql_cxt.func_tableof_index = NIL;
    foreach (arg, fcache->args) {
        ExprState* argstate = (ExprState*)lfirst(arg);

        fcinfo->argTypes[i] = argstate->resultType;
        if (has_refcursor && fcinfo->argTypes[i] == REFCURSOROID)
            econtext->is_cursor = true;
        fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo->argnull[i], NULL);
        if (is_huge_clob(fcinfo->argTypes[i], fcinfo->argnull[i], fcinfo->arg[i])) {
            is_have_huge_clob = true;
        }
        ExecTableOfIndexInfo execTableOfIndexInfo;
        initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext);
        ExecEvalParamExternTableOfIndex((Node*)argstate->expr, &execTableOfIndexInfo);
        if (execTableOfIndexInfo.tableOfIndex != NULL) {
            if (!IsTableOfFunc(fcache->func.fn_oid)) {
                MemoryContext oldCxt = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER));
                PLpgSQL_func_tableof_index* func_tableof =
                    (PLpgSQL_func_tableof_index*)palloc0(sizeof(PLpgSQL_func_tableof_index));
                func_tableof->varno = i;
                func_tableof->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType;
                func_tableof->tableOfIndex = copyTableOfIndex(execTableOfIndexInfo.tableOfIndex);
                u_sess->plsql_cxt.func_tableof_index = lappend(u_sess->plsql_cxt.func_tableof_index, func_tableof);
                MemoryContextSwitchTo(oldCxt);
            }

            u_sess->SPI_cxt.cur_tableof_index->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType;
            u_sess->SPI_cxt.cur_tableof_index->tableOfIndex = execTableOfIndexInfo.tableOfIndex;
            u_sess->SPI_cxt.cur_tableof_index->tableOfNestLayer = execTableOfIndexInfo.tableOfLayers;
            /* for nest table of output, save layer of this var tableOfGetNestLayer in ExecEvalArrayRef,
            or set to zero for get whole nest table. */
            u_sess->SPI_cxt.cur_tableof_index->tableOfGetNestLayer = -1;
        }

        if (has_refcursor && econtext->is_cursor) {
            var_dno[i] = econtext->dno;
            CopyCursorInfoData(&fcinfo->refcursor_data.argCursor[i], &econtext->cursor_data);
        }
        econtext->is_cursor = false;
        i++;
    }

    /*
     * If function is strict, and there are any NULL arguments, skip calling
     * the function and return NULL.
     */
    if (fcache->func.fn_strict) {
        while (--i >= 0) {
            if (fcinfo->argnull[i]) {
                *isNull = true;
                u_sess->SPI_cxt.is_stp = savedIsSTP;
                u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet;
                if (needResetErrMsg) {
                    stp_reset_commit_rolback_err_msg();
                }
                return (Datum)0;
            }
        }
    }

    pgstat_init_function_usage(fcinfo, &fcusage);

    fcinfo->isnull = false;
    check_huge_clob_paramter(fcinfo, is_have_huge_clob);
    if (u_sess->instr_cxt.global_instr != NULL && fcinfo->flinfo->fn_addr == plpgsql_call_handler) {
        StreamInstrumentation* save_global_instr = u_sess->instr_cxt.global_instr;
        u_sess->instr_cxt.global_instr = NULL;
        result = FunctionCallInvoke(fcinfo);   // node will be free at here or else;
        u_sess->instr_cxt.global_instr = save_global_instr;
    } else {
        if (fcinfo->argTypes[0] == CLOBOID && fcinfo->argTypes[1] == CLOBOID && fcinfo->flinfo->fn_addr == textcat) {
            bool is_null = false;
            if (fcinfo->arg[0] != 0 && VARATT_IS_EXTERNAL_LOB(fcinfo->arg[0])) {
                struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(fcinfo->arg[0]));
                fcinfo->arg[0] = fetch_lob_value_from_tuple(lob_pointer, InvalidOid, &is_null);
            }
            if (fcinfo->arg[1] != 0 && VARATT_IS_EXTERNAL_LOB(fcinfo->arg[1])) {
                struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(fcinfo->arg[1]));
                fcinfo->arg[1] = fetch_lob_value_from_tuple(lob_pointer, InvalidOid, &is_null);
            }
        }    
        result = FunctionCallInvoke(fcinfo);
    }
    *isNull = fcinfo->isnull;

    if (has_refcursor && econtext->plpgsql_estate != NULL) {
        PLpgSQL_execstate* estate = econtext->plpgsql_estate;
        for (i = 0; i < fcinfo->nargs; i++) {
            /* copy in-args cursor option info */
            if (var_dno[i] >= 0) {
                int dno = var_dno[i];
                Cursor_Data* cursor_data = &fcinfo->refcursor_data.argCursor[i];
#ifdef USE_ASSERT_CHECKING
                PLpgSQL_datum* datum = estate->datums[dno];
#endif
                Assert(datum->dtype == PLPGSQL_DTYPE_VAR);
                Assert(((PLpgSQL_var*)datum)->datatype->typoid == REFCURSOROID);

                ExecCopyDataToDatum(estate->datums, dno, cursor_data);
            }
        }

        if (fcinfo->flinfo->fn_rettype == REFCURSOROID) {
            /* copy function returns cursor option info.
             * for simple expr in exec_eval_expr, we can not get the result type,
             * so cursor_return_data mallocs here.
             */
            if (estate->cursor_return_data == NULL) {
                estate->cursor_return_data = (Cursor_Data*)palloc0(sizeof(Cursor_Data));
                estate->cursor_return_numbers = 1;
            }
            int rc = memcpy_s(estate->cursor_return_data,
                sizeof(Cursor_Data),
                fcinfo->refcursor_data.returnCursor,
                sizeof(Cursor_Data));
            securec_check(rc, "\0", "\0");
        }
    }

    pgstat_end_function_usage(&fcusage, true);

    if (has_refcursor) {
        if (fcinfo->refcursor_data.argCursor != NULL)
            pfree_ext(fcinfo->refcursor_data.argCursor);
        if (var_dno != NULL)
            pfree_ext(var_dno);
    }

    u_sess->SPI_cxt.is_stp = savedIsSTP;
    u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet;
    if (needResetErrMsg) {
        stp_reset_commit_rolback_err_msg();
    }

    set_result_for_plpgsql_language_function_with_outparam(fcache, &result, isNull);

    return result;
}

/*
 * @Description: jugde function has parameter that is refcursor or return type is refcursor
 * @in Funcid - function oid
 * @in fcinfo - function call info
 * @return - has refcursor
 */
static bool func_has_refcursor_args(Oid Funcid, FunctionCallInfoData* fcinfo)
{
    HeapTuple proctup = NULL;
    Form_pg_proc procStruct;
    int allarg;
    Oid* p_argtypes = NULL;
    char** p_argnames = NULL;
    char* p_argmodes = NULL;
    bool use_cursor = false;
    bool return_refcursor = false;
    int out_count = 0; /* out arg count */

    proctup = SearchSysCache(PROCOID, ObjectIdGetDatum(Funcid), 0, 0, 0);

    /*
     * function may be deleted after clist be searched.
     */
    if (!HeapTupleIsValid(proctup)) {
        ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function doesn't exist ")));
    }

    /* get the all args informations, only "in" parameters if p_argmodes is null */
    allarg = get_func_arg_info(proctup, &p_argtypes, &p_argnames, &p_argmodes);
    procStruct = (Form_pg_proc)GETSTRUCT(proctup);

    fcinfo->refcursor_data.return_number = 0;
    fcinfo->refcursor_data.returnCursor = NULL;
    for (int i = 0; i < allarg; i++) {
        if (p_argmodes != NULL && (p_argmodes[i] == 'o' || p_argmodes[i] == 'b')) {
            out_count++;
            if (p_argtypes[i] == REFCURSOROID)
                return_refcursor = true;
        } else {
            if (p_argtypes[i] == REFCURSOROID)
                use_cursor = true;
        }
    }

    if (procStruct->prorettype == REFCURSOROID) {
        use_cursor = true;
        fcinfo->refcursor_data.return_number = 1;
    } else if (return_refcursor) {
        fcinfo->refcursor_data.return_number = out_count;
    }

    ReleaseSysCache(proctup);
    return use_cursor;
}
/*
 *		ExecMakeTableFunctionResult
 *
 * Evaluate a table function, producing a materialized result in a Tuplestore
 * object.
 */
Tuplestorestate* ExecMakeTableFunctionResult(
    ExprState* funcexpr, ExprContext* econtext, TupleDesc expectedDesc, bool randomAccess, FunctionScanState* node)
{
    Tuplestorestate* tupstore = NULL;
    TupleDesc tupdesc = NULL;
    Oid funcrettype;
    bool returnsTuple = false;
    bool returnsSet = false;
    FunctionCallInfoData fcinfo;
    PgStat_FunctionCallUsage fcusage;
    ReturnSetInfo rsinfo;
    HeapTupleData tmptup;
    MemoryContext callerContext;
    MemoryContext oldcontext;
    bool direct_function_call = false;
    bool first_time = true;
    int* var_dno = NULL;
    bool has_refcursor = false;
    bool has_out_param = false;

    FuncExpr *fexpr = NULL;
    bool savedIsSTP = u_sess->SPI_cxt.is_stp;
    bool savedProConfigIsSet = u_sess->SPI_cxt.is_proconfig_set;
    bool proIsProcedure = false;
    bool supportTranaction = false;
#ifdef ENABLE_MULTIPLE_NODES
    if (IS_PGXC_COORDINATOR && (t_thrd.proc->workingVersionNum  >= STP_SUPPORT_COMMIT_ROLLBACK)) {
        supportTranaction = true;
    }
#else
    supportTranaction = true;
#endif
    bool needResetErrMsg = (u_sess->SPI_cxt.forbidden_commit_rollback_err_msg[0] == '\0');

    /* Only allow commit at CN, therefore only need to set atomic and relevant check at CN level. */
    if (supportTranaction && IsA(funcexpr->expr, FuncExpr)) {
        fexpr = (FuncExpr*)funcexpr->expr;
        char prokind = (reinterpret_cast<FuncExprState*>(funcexpr))->prokind;
        if (!u_sess->SPI_cxt.is_allow_commit_rollback) {
            node->atomic = true;
        }
        else if (IsAfterTriggerBegin()) {
            node->atomic = true;
            stp_set_commit_rollback_err_msg(STP_XACT_AFTER_TRIGGER_BEGIN);
        }
        /*
         * If proconfig is set we can't allow transaction commands because of the
         * way the GUC stacking works: The transaction boundary would have to pop
         * the proconfig setting off the stack.  That restriction could be lifted
         * by redesigning the GUC nesting mechanism a bit.
         */
        if (!prokind) {
            HeapTuple tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid));
            bool isNull = false;
            if (!HeapTupleIsValid(tp)) {
                elog(ERROR, "cache lookup failed for function %u", fexpr->funcid);
            }

            /* immutable or stable function do not support commit/rollback */
            bool isNullVolatile = false;
            Datum provolatile = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_provolatile, &isNullVolatile);
            if (!isNullVolatile && CharGetDatum(provolatile) != PROVOLATILE_VOLATILE) {
                node->atomic = true;
                stp_set_commit_rollback_err_msg(STP_XACT_IMMUTABLE);
            }

            Datum datum = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_prokind, &isNull);
            proIsProcedure = PROC_IS_PRO(CharGetDatum(datum));
            if (proIsProcedure) {
                (reinterpret_cast<FuncExprState*>(funcexpr))->prokind = 'p';
            } else {
                (reinterpret_cast<FuncExprState*>(funcexpr))->prokind = 'f';
            }
            /* if proIsProcedure means it was a stored procedure */
            u_sess->SPI_cxt.is_stp = savedIsSTP;
            if (!heap_attisnull(tp, Anum_pg_proc_proconfig, NULL) || u_sess->SPI_cxt.is_proconfig_set) {
                u_sess->SPI_cxt.is_proconfig_set = true;
                node->atomic = true;
                stp_set_commit_rollback_err_msg(STP_XACT_GUC_IN_OPT_CLAUSE);
            }
            ReleaseSysCache(tp);
        } else {
            proIsProcedure = PROC_IS_PRO(prokind);
            u_sess->SPI_cxt.is_stp = savedIsSTP;
        }
    }

    callerContext = CurrentMemoryContext;

    if (unlikely(funcexpr == NULL)) {
        ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("The input function expression is NULL.")));
    }
    funcrettype = exprType((Node*)funcexpr->expr);

    returnsTuple = type_is_rowtype(funcrettype);
    econtext->plpgsql_estate = plpgsql_estate;
    plpgsql_estate = NULL;

    /*
     * Prepare a resultinfo node for communication.  We always do this even if
     * not expecting a set result, so that we can pass expectedDesc.  In the
     * generic-expression case, the expression doesn't actually get to see the
     * resultinfo, but set it up anyway because we use some of the fields as
     * our own state variables.
     */
    rsinfo.type = T_ReturnSetInfo;
    rsinfo.econtext = econtext;
    rsinfo.expectedDesc = expectedDesc;
    rsinfo.allowedModes = (int)(SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred);
    if (randomAccess)
        rsinfo.allowedModes |= (int)SFRM_Materialize_Random;
    rsinfo.returnMode = SFRM_ValuePerCall;
    /* isDone is filled below */
    rsinfo.setResult = NULL;
    rsinfo.setDesc = NULL;

    /*
     * Normally the passed expression tree will be a FuncExprState, since the
     * grammar only allows a function call at the top level of a table
     * function reference.	However, if the function doesn't return set then
     * the planner might have replaced the function call via constant-folding
     * or inlining.  So if we see any other kind of expression node, execute
     * it via the general ExecEvalExpr() code; the only difference is that we
     * don't get a chance to pass a special ReturnSetInfo to any functions
     * buried in the expression.
     */
    if (funcexpr && IsA(funcexpr, FuncExprState) && IsA(funcexpr->expr, FuncExpr)) {
        FuncExprState* fcache = (FuncExprState*)funcexpr;
        ExprDoneCond argDone;

        /*
         * This path is similar to ExecMakeFunctionResult.
         */
        direct_function_call = true;

        /*
         * Initialize function cache if first time through
         */
        if (fcache->func.fn_oid == InvalidOid) {
            FuncExpr* func = (FuncExpr*)fcache->xprstate.expr;

            init_fcache<false>(func->funcid, func->inputcollid, fcache, econtext->ecxt_per_query_memory, false);
        }
        returnsSet = fcache->func.fn_retset;
        InitFunctionCallInfoData(fcinfo,
            &(fcache->func),
            list_length(fcache->args),
            fcache->fcinfo_data.fncollation,
            (Node*)node,
            (Node*)&rsinfo);

        has_refcursor = func_has_refcursor_args(fcinfo.flinfo->fn_oid, &fcinfo);

        has_out_param = (is_function_with_plpgsql_language_and_outparam(fcinfo.flinfo->fn_oid) != InvalidOid);
        if (u_sess->attr.attr_sql.sql_compatibility == A_FORMAT && has_out_param) {
            returnsTuple = type_is_rowtype(RECORDOID);
        }

        int cursor_return_number = fcinfo.refcursor_data.return_number;
        if (cursor_return_number > 0) {
            /* init returnCursor to store out-args cursor info on FunctionScan context*/
            fcinfo.refcursor_data.returnCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * cursor_return_number);
        } else {
            fcinfo.refcursor_data.returnCursor = NULL;
        }

        if (has_refcursor) {
            /* init argCursor to store in-args cursor info on FunctionScan context*/
            fcinfo.refcursor_data.argCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo.nargs);
            var_dno = (int*)palloc0(sizeof(int) * fcinfo.nargs);
            int rc = memset_s(var_dno, sizeof(int) * fcinfo.nargs, -1, sizeof(int) * fcinfo.nargs);
            securec_check(rc, "\0", "\0");
        }

        /*
         * Evaluate the function's argument list.
         *
         * Note: ideally, we'd do this in the per-tuple context, but then the
         * argument values would disappear when we reset the context in the
         * inner loop.	So do it in caller context.  Perhaps we should make a
         * separate context just to hold the evaluated arguments?
         */
        if (has_refcursor)
            argDone = ExecEvalFuncArgs<true>(&fcinfo, fcache->args, econtext, var_dno);
        else
            argDone = ExecEvalFuncArgs<false>(&fcinfo, fcache->args, econtext);
        /* We don't allow sets in the arguments of the table function */
        if (argDone != ExprSingleResult)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("set-valued function called in context that cannot accept a set")));

        /*
         * If function is strict, and there are any NULL arguments, skip
         * calling the function and act like it returned NULL (or an empty
         * set, in the returns-set case).
         */
        if (fcache->func.fn_strict) {
            int i;

            for (i = 0; i < fcinfo.nargs; i++) {
                if (fcinfo.argnull[i])
                    goto no_function_result;
            }
        }
    } else {
        /* Treat funcexpr as a generic expression */
        direct_function_call = false;
        InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, (Node*)node, NULL);
    }

    /*
     * Switch to short-lived context for calling the function or expression.
     */
    MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);

    /*
     * Loop to handle the ValuePerCall protocol (which is also the same
     * behavior needed in the generic ExecEvalExpr path).
     */
    for (;;) {
        Datum result;

        CHECK_FOR_INTERRUPTS();

        /*
         * reset per-tuple memory context before each call of the function or
         * expression. This cleans up any local memory the function may leak
         * when called.
         */
        ResetExprContext(econtext);

        /* Call the function or expression one time */
        if (direct_function_call) {
            pgstat_init_function_usage(&fcinfo, &fcusage);

            fcinfo.isnull = false;
            rsinfo.isDone = ExprSingleResult;
            result = FunctionCallInvoke(&fcinfo);

            if (econtext->plpgsql_estate != NULL) {
                PLpgSQL_execstate* estate = econtext->plpgsql_estate;
                bool isVaildReturn = (fcinfo.refcursor_data.return_number > 0 &&
                    estate->cursor_return_data != NULL && fcinfo.refcursor_data.returnCursor != NULL);
                if (isVaildReturn) {
                    bool isVaildReturnNum = (fcinfo.refcursor_data.return_number > estate->cursor_return_numbers);
                    if (isVaildReturnNum) {
                        pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult);
                        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmodule(MOD_PLSQL),
                            errmsg("The expected output of the cursor:%d and function:%d does not match",
                            estate->cursor_return_numbers, fcinfo.refcursor_data.return_number)));
                    }
                    for (int i = 0; i < fcinfo.refcursor_data.return_number; i++) {
                        CopyCursorInfoData(&estate->cursor_return_data[i], &fcinfo.refcursor_data.returnCursor[i]);
                    }
                }

                if (var_dno != NULL) {
                    for (int i = 0; i < fcinfo.nargs; i++) {
                        if (var_dno[i] >= 0) {
                            int dno = var_dno[i];
                            Cursor_Data* cursor_data = &fcinfo.refcursor_data.argCursor[i];
                            PLpgSQL_execstate* execstate = econtext->plpgsql_estate;
#ifdef USE_ASSERT_CHECKING
                            PLpgSQL_datum* datum = execstate->datums[dno];
#endif
                            Assert(datum->dtype == PLPGSQL_DTYPE_VAR);
                            Assert(((PLpgSQL_var*)datum)->datatype->typoid == REFCURSOROID);

                            ExecCopyDataToDatum(execstate->datums, dno, cursor_data);
                        }
                    }
                }
            }

            pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult);
        } else {
            result = ExecEvalExpr(funcexpr, econtext, &fcinfo.isnull, &rsinfo.isDone);
        }

        /* Which protocol does function want to use? */
        if (rsinfo.returnMode == SFRM_ValuePerCall) {
            /*
             * Check for end of result set.
             */
            if (rsinfo.isDone == ExprEndResult) {
                break;
            }

            /*
             * Can't do anything very useful with NULL rowtype values. For a
             * function returning set, we consider this a protocol violation
             * (but another alternative would be to just ignore the result and
             * "continue" to get another row).	For a function not returning
             * set, we fall out of the loop; we'll cons up an all-nulls result
             * row below.
             */
            if (returnsTuple && fcinfo.isnull && !has_out_param) {
                if (!returnsSet) {
                    break;
                }
                ereport(ERROR,
                    (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                        errmsg("function returning set of rows cannot return null value")));
            }

            /*
             * If first time through, build tupdesc and tuplestore for result
             */
            if (first_time) {
                oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
                if (returnsTuple) {
                    /*
                     * Use the type info embedded in the rowtype Datum to look
                     * up the needed tupdesc.  Make a copy for the query.
                     */
                    HeapTupleHeader td;

                    td = DatumGetHeapTupleHeader(result);
                    if (IsA(funcexpr->expr, Const)) {
                        tupdesc = lookup_rowtype_tupdesc_copy(
                            ((Const*)funcexpr->expr)->consttype, HeapTupleHeaderGetTypMod(td));
                    } else {
                        tupdesc =
                            lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td));
                    }
                } else {
                    /*
                     * Scalar type, so make a single-column descriptor
                     */
                    tupdesc = CreateTemplateTupleDesc(1, false, TAM_HEAP);
                    TupleDescInitEntry(tupdesc, (AttrNumber)1, "column", funcrettype, -1, 0);
                }
                tupstore = tuplestore_begin_heap(randomAccess, false, u_sess->attr.attr_memory.work_mem);
                MemoryContextSwitchTo(oldcontext);
                rsinfo.setResult = tupstore;
                rsinfo.setDesc = tupdesc;
            }

            /*
             * Store current resultset item.
             */
            if (returnsTuple) {
                HeapTupleHeader td;

                td = DatumGetHeapTupleHeader(result);

                /*
                 * Verify all returned rows have same subtype; necessary in
                 * case the type is RECORD.
                 */
                if ((HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid ||
                        HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod) &&
                    nodeTag(funcexpr->expr) != T_Const) {
                        ereport(ERROR,
                            (errcode(ERRCODE_DATATYPE_MISMATCH),
                                errmsg("rows returned by function are not all of the same row type"),
                                errdetail("return type id %u, tuple decription id %u, return typmod %d  "
                                          "tuple decription, typmod %d",
                                    HeapTupleHeaderGetTypeId(td),
                                    tupdesc->tdtypeid,
                                    HeapTupleHeaderGetTypMod(td),
                                    tupdesc->tdtypmod)));
                    }

                /*
                 * tuplestore_puttuple needs a HeapTuple not a bare
                 * HeapTupleHeader, but it doesn't need all the fields.
                 */
                tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
                tmptup.t_data = td;

                tuplestore_puttuple(tupstore, &tmptup);
            } else {
                tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull);
            }

            /*
             * Are we done?
             */
            if (rsinfo.isDone != ExprMultipleResult) {
                break;
            }
        } else if (rsinfo.returnMode == SFRM_Materialize) {
            /* check we're on the same page as the function author */
            if (!first_time || rsinfo.isDone != ExprSingleResult) {
                ereport(ERROR,
                    (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
                        errmsg("table-function protocol for materialize mode was not followed")));
            }
            /* Done evaluating the set result */
            break;
        } else {
            ereport(ERROR,
                (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
                    errmsg("unrecognized table-function returnMode: %d", (int)rsinfo.returnMode)));
        }

        first_time = false;
    }

no_function_result:

    /*
     * If we got nothing from the function (ie, an empty-set or NULL result),
     * we have to create the tuplestore to return, and if it's a
     * non-set-returning function then insert a single all-nulls row.
     */
    if (rsinfo.setResult == NULL) {
        MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
        tupstore = tuplestore_begin_heap(randomAccess, false, u_sess->attr.attr_memory.work_mem);
        rsinfo.setResult = tupstore;
        if (!returnsSet) {
            int natts = expectedDesc->natts;
            Datum* nulldatums = NULL;
            bool* nullflags = NULL;
            errno_t rc = EOK;

            MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
            nulldatums = (Datum*)palloc0(natts * sizeof(Datum));
            nullflags = (bool*)palloc(natts * sizeof(bool));
            rc = memset_s(nullflags, natts * sizeof(bool), true, natts * sizeof(bool));
            securec_check(rc, "\0", "\0");
            MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
            tuplestore_putvalues(tupstore, expectedDesc, nulldatums, nullflags);
        }
    }

    /*
     * If function provided a tupdesc, cross-check it.	We only really need to
     * do this for functions returning RECORD, but might as well do it always.
     */
    if (rsinfo.setDesc) {
        tupledesc_match(expectedDesc, rsinfo.setDesc);

        /*
         * If it is a dynamically-allocated TupleDesc, free it: it is
         * typically allocated in a per-query context, so we must avoid
         * leaking it across multiple usages.
         */
        if (rsinfo.setDesc->tdrefcount == -1)
            FreeTupleDesc(rsinfo.setDesc);
    }

    MemoryContextSwitchTo(callerContext);
    econtext->plpgsql_estate = NULL;

    if (has_refcursor) {
        if (fcinfo.refcursor_data.argCursor != NULL)
            pfree_ext(fcinfo.refcursor_data.argCursor);
        if (fcinfo.refcursor_data.returnCursor != NULL)
            pfree_ext(fcinfo.refcursor_data.returnCursor);
        if (var_dno != NULL)
            pfree_ext(var_dno);
    }

    /* reset the u_sess->SPI_cxt.is_stp, u_sess->SPI_cxt.is_proconfig_set 
       and error message value */
    u_sess->SPI_cxt.is_stp = savedIsSTP;
    u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet;
    if (needResetErrMsg) {
        stp_reset_commit_rolback_err_msg();
    }

    /* All done, pass back the tuplestore */
    return rsinfo.setResult;
}

/* ----------------------------------------------------------------
 *		ExecEvalFunc
 *		ExecEvalOper
 *
 *		Evaluate the functional result of a list of arguments by calling the
 *		function manager.
 * ----------------------------------------------------------------
 */
/* ----------------------------------------------------------------
 *		ExecEvalFunc
 * ----------------------------------------------------------------
 */
static Datum ExecEvalFunc(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    /* This is called only the first time through */
    FuncExpr* func = (FuncExpr*)fcache->xprstate.expr;
    Oid target_type = InvalidOid;
    Oid source_type = InvalidOid;

    /* Initialize function lookup info */
    init_fcache<false>(func->funcid, func->inputcollid, fcache, econtext->ecxt_per_query_memory, true);

    bool has_refcursor = func_has_refcursor_args(func->funcid, &fcache->fcinfo_data);
    int cursor_return_number = fcache->fcinfo_data.refcursor_data.return_number;

    if (func->funcformat == COERCE_EXPLICIT_CAST || func->funcformat == COERCE_IMPLICIT_CAST) {
        target_type = func->funcresulttype;
        source_type = fcache->fcinfo_data.argTypes[0];

        HeapTuple proc_tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(func->funcid), 0, 0, 0);
        if (HeapTupleIsValid(proc_tuple)) {
            Form_pg_proc proc_struct = (Form_pg_proc)GETSTRUCT(proc_tuple);
            source_type = proc_struct->proargtypes.values[0];
            ReleaseSysCache(proc_tuple);
        }
        HeapTuple cast_tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(source_type),
                                                ObjectIdGetDatum(target_type));

        if (HeapTupleIsValid(cast_tuple)) {
            Relation cast_rel = heap_open(CastRelationId, AccessShareLock);
            int castowner_Anum = Anum_pg_cast_castowner;
            if (castowner_Anum <= (int)HeapTupleHeaderGetNatts(cast_tuple->t_data, cast_rel->rd_att)) {
                bool isnull = true;
                Datum datum = fastgetattr(cast_tuple, Anum_pg_cast_castowner, cast_rel->rd_att, &isnull);
                if (!isnull) {
                    u_sess->exec_cxt.cast_owner = DatumGetObjectId(datum);
                } else {
                    u_sess->exec_cxt.cast_owner = InvalidCastOwnerId;
                }
            }
            heap_close(cast_rel, AccessShareLock);
            ReleaseSysCache(cast_tuple);
        }
    }

    /*
     * We need to invoke ExecMakeFunctionResult if either the function itself
     * or any of its input expressions can return a set.  Otherwise, invoke
     * ExecMakeFunctionResultNoSets.  In either case, change the evalfunc
     * pointer to go directly there on subsequent uses.
     */
    if (fcache->func.fn_retset) {
        if (has_refcursor) {
            if (cursor_return_number > 0) {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, true, true>;
                return ExecMakeFunctionResult<true, true, true>(fcache, econtext, isNull, isDone);
            } else {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, false, true>;
                return ExecMakeFunctionResult<true, false, true>(fcache, econtext, isNull, isDone);
            }
        } else {
            if (cursor_return_number > 0) {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, true, true>;
                return ExecMakeFunctionResult<false, true, true>(fcache, econtext, isNull, isDone);
            } else {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, false, true>;
                return ExecMakeFunctionResult<false, false, true>(fcache, econtext, isNull, isDone);
            }
        }
    } else if (expression_returns_set((Node*)func->args)) {
        if (has_refcursor) {
            if (cursor_return_number > 0) {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, true, false>;
                return ExecMakeFunctionResult<true, true, false>(fcache, econtext, isNull, isDone);
            } else {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, false, false>;
                return ExecMakeFunctionResult<true, false, false>(fcache, econtext, isNull, isDone);
            }
        } else {
            if (cursor_return_number > 0) {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, true, false>;
                return ExecMakeFunctionResult<false, true, false>(fcache, econtext, isNull, isDone);
            } else {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, false, false>;
                return ExecMakeFunctionResult<false, false, false>(fcache, econtext, isNull, isDone);
            }
        }
    } else {
        if (has_refcursor) {
            if (cursor_return_number > 0) {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, true>;
                return ExecMakeFunctionResultNoSets<true, true>(fcache, econtext, isNull, isDone);
            } else {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, false>;
                return ExecMakeFunctionResultNoSets<true, false>(fcache, econtext, isNull, isDone);
            }
        } else {
            if (cursor_return_number > 0) {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, true>;
                return ExecMakeFunctionResultNoSets<false, true>(fcache, econtext, isNull, isDone);
            } else {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, false>;
                return ExecMakeFunctionResultNoSets<false, false>(fcache, econtext, isNull, isDone);
            }
        }
    }
}

/* ----------------------------------------------------------------
 *		ExecEvalOper
 * ----------------------------------------------------------------
 */
static Datum ExecEvalOper(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    /* This is called only the first time through */
    OpExpr* op = (OpExpr*)fcache->xprstate.expr;
    bool has_refcursor = false;

    /* Initialize function lookup info */
    init_fcache<false>(op->opfuncid, op->inputcollid, fcache, econtext->ecxt_per_query_memory, true);
    has_refcursor = func_has_refcursor_args(op->opfuncid, &fcache->fcinfo_data);
    int cursor_return_number = fcache->fcinfo_data.refcursor_data.return_number;

    /*
     * We need to invoke ExecMakeFunctionResult if either the function itself
     * or any of its input expressions can return a set.  Otherwise, invoke
     * ExecMakeFunctionResultNoSets.  In either case, change the evalfunc
     * pointer to go directly there on subsequent uses.
     */
     if (fcache->func.fn_retset) {
        if (has_refcursor) {
            if (cursor_return_number > 0) {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, true, true>;
                return ExecMakeFunctionResult<true, true, true>(fcache, econtext, isNull, isDone);
            } else {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, false, true>;
                return ExecMakeFunctionResult<true, false, true>(fcache, econtext, isNull, isDone);
            }
        } else {
            if (cursor_return_number > 0) {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, true, true>;
                return ExecMakeFunctionResult<false, true, true>(fcache, econtext, isNull, isDone);
            } else {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, false, true>;
                return ExecMakeFunctionResult<false, false, true>(fcache, econtext, isNull, isDone);
            }
        }
    } else if (expression_returns_set((Node*)op->args)) {
        if (has_refcursor) {
            if (cursor_return_number > 0) {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, true, false>;
                return ExecMakeFunctionResult<true, true, false>(fcache, econtext, isNull, isDone);
            } else {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, false, false>;
                return ExecMakeFunctionResult<true, false, false>(fcache, econtext, isNull, isDone);
            }
        } else {
            if (cursor_return_number > 0) {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, true, false>;
                return ExecMakeFunctionResult<false, true, false>(fcache, econtext, isNull, isDone);
            } else {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, false, false>;
                return ExecMakeFunctionResult<false, false, false>(fcache, econtext, isNull, isDone);
            }
        }
    } else {
        if (has_refcursor) {
            if (cursor_return_number > 0) {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, true>;
                return ExecMakeFunctionResultNoSets<true, true>(fcache, econtext, isNull, isDone);
            } else {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, false>;
                return ExecMakeFunctionResultNoSets<true, false>(fcache, econtext, isNull, isDone);
            }
        } else {
            if (cursor_return_number > 0) {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, true>;
                return ExecMakeFunctionResultNoSets<false, true>(fcache, econtext, isNull, isDone);
            } else {
                fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, false>;
                return ExecMakeFunctionResultNoSets<false, false>(fcache, econtext, isNull, isDone);
            }
        }
    }
}

/* ----------------------------------------------------------------
 *		ExecEvalDistinct
 *
 * IS DISTINCT FROM must evaluate arguments to determine whether
 * they are NULL; if either is NULL then the result is already
 * known. If neither is NULL, then proceed to evaluate the
 * function. Note that this is *always* derived from the equals
 * operator, but since we need special processing of the arguments
 * we can not simply reuse ExecEvalOper() or ExecEvalFunc().
 * ----------------------------------------------------------------
 */
static Datum ExecEvalDistinct(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    Datum result;
    FunctionCallInfo fcinfo;
    ExprDoneCond argDone;

    /* Set default values for result flags: non-null, not a set result */
    *isNull = false;
    if (isDone != NULL)
        *isDone = ExprSingleResult;

    /*
     * Initialize function cache if first time through
     */
    if (fcache->func.fn_oid == InvalidOid) {
        DistinctExpr* op = (DistinctExpr*)fcache->xprstate.expr;

        init_fcache<false>(op->opfuncid, op->inputcollid, fcache, econtext->ecxt_per_query_memory, true);
        Assert(!fcache->func.fn_retset);
    }

    /*
     * Evaluate arguments
     */
    fcinfo = &fcache->fcinfo_data;
    argDone = ExecEvalFuncArgs<false>(fcinfo, fcache->args, econtext);
    if (argDone != ExprSingleResult)
        ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("IS DISTINCT FROM does not support set arguments")));
    Assert(fcinfo->nargs == 2);

    if (fcinfo->argnull[0] && fcinfo->argnull[1]) {
        /* Both NULL? Then is not distinct... */
        result = BoolGetDatum(FALSE);
    } else if (fcinfo->argnull[0] || fcinfo->argnull[1]) {
        /* Only one is NULL? Then is distinct... */
        result = BoolGetDatum(TRUE);
    } else {
        fcinfo->isnull = false;
        result = FunctionCallInvoke(fcinfo);
        *isNull = fcinfo->isnull;
        /* Must invert result of "=" */
        result = BoolGetDatum(!DatumGetBool(result));
    }

    return result;
}

/*
 * ExecEvalScalarArrayOp
 *
 * Evaluate "scalar op ANY/ALL (array)".  The operator always yields boolean,
 * and we combine the results across all array elements using OR and AND
 * (for ANY and ALL respectively).	Of course we short-circuit as soon as
 * the result is known.
 */
static Datum ExecEvalScalarArrayOp(
    ScalarArrayOpExprState* sstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    ScalarArrayOpExpr* opexpr = (ScalarArrayOpExpr*)sstate->fxprstate.xprstate.expr;
    bool useOr = opexpr->useOr;
    ArrayType* arr = NULL;
    int nitems;
    Datum result;
    bool resultnull = false;
    FunctionCallInfo fcinfo;
    ExprDoneCond argDone;
    int i;
    int16 typlen;
    bool typbyval = false;
    char typalign;
    char* s = NULL;
    bits8* bitmap = NULL;
    int bitmask;

    /* Set default values for result flags: non-null, not a set result */
    *isNull = false;
    if (isDone != NULL)
        *isDone = ExprSingleResult;

    /*
     * Initialize function cache if first time through
     */
    if (sstate->fxprstate.func.fn_oid == InvalidOid) {
        init_fcache<false>(
            opexpr->opfuncid, opexpr->inputcollid, &sstate->fxprstate, econtext->ecxt_per_query_memory, true);
        Assert(!sstate->fxprstate.func.fn_retset);
    }

    /*
     * Evaluate arguments
     */
    fcinfo = &sstate->fxprstate.fcinfo_data;
    /* init the number of arguments to a function. */
    InitFunctionCallInfoArgs(*fcinfo, 2, 1);
    argDone = ExecEvalFuncArgs<false>(fcinfo, sstate->fxprstate.args, econtext);
    if (argDone != ExprSingleResult)
        ereport(
            ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("op ANY/ALL (array) does not support set arguments")));
    Assert(fcinfo->nargs == 2);

    /*
     * If the array is NULL then we return NULL --- it's not very meaningful
     * to do anything else, even if the operator isn't strict.
     */
    if (fcinfo->argnull[1]) {
        *isNull = true;
        return (Datum)0;
    }
    /* Else okay to fetch and detoast the array */
    arr = DatumGetArrayTypeP(fcinfo->arg[1]);

    /*
     * If the array is empty, we return either FALSE or TRUE per the useOr
     * flag.  This is correct even if the scalar is NULL; since we would
     * evaluate the operator zero times, it matters not whether it would want
     * to return NULL.
     */
    nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
    if (nitems <= 0)
        return BoolGetDatum(!useOr);

    /*
     * If the scalar is NULL, and the function is strict, return NULL; no
     * point in iterating the loop.
     */
    if (fcinfo->argnull[0] && sstate->fxprstate.func.fn_strict) {
        *isNull = true;
        return (Datum)0;
    }

    /*
     * We arrange to look up info about the element type only once per series
     * of calls, assuming the element type doesn't change underneath us.
     */
    if (sstate->element_type != ARR_ELEMTYPE(arr)) {
        get_typlenbyvalalign(ARR_ELEMTYPE(arr), &sstate->typlen, &sstate->typbyval, &sstate->typalign);
        sstate->element_type = ARR_ELEMTYPE(arr);
    }
    typlen = sstate->typlen;
    typbyval = sstate->typbyval;
    typalign = sstate->typalign;

    result = BoolGetDatum(!useOr);
    resultnull = false;

    /* Loop over the array elements */
    s = (char*)ARR_DATA_PTR(arr);
    bitmap = ARR_NULLBITMAP(arr);
    bitmask = 1;

    for (i = 0; i < nitems; i++) {
        Datum elt;
        Datum thisresult;

        /* Get array element, checking for NULL */
        if (bitmap && (*bitmap & bitmask) == 0) {
            fcinfo->arg[1] = (Datum)0;
            fcinfo->argnull[1] = true;
        } else {
            elt = fetch_att(s, typbyval, typlen);
            s = att_addlength_pointer(s, typlen, s);
            s = (char*)att_align_nominal(s, typalign);
            fcinfo->arg[1] = elt;
            fcinfo->argnull[1] = false;
            fcinfo->argTypes[1] = ARR_ELEMTYPE(arr);
        }

        /* Call comparison function */
        if (fcinfo->argnull[1] && sstate->fxprstate.func.fn_strict) {
            fcinfo->isnull = true;
            thisresult = (Datum)0;
        } else {
            fcinfo->isnull = false;
            thisresult = FunctionCallInvoke(fcinfo);
        }

        /* Combine results per OR or AND semantics */
        if (fcinfo->isnull)
            resultnull = true;
        else if (useOr) {
            if (DatumGetBool(thisresult)) {
                result = BoolGetDatum(true);
                resultnull = false;
                break; /* needn't look at any more elements */
            }
        } else {
            if (!DatumGetBool(thisresult)) {
                result = BoolGetDatum(false);
                resultnull = false;
                break; /* needn't look at any more elements */
            }
        }

        /* advance bitmap pointer if any */
        if (bitmap != NULL) {
            bitmask <<= 1;
            if (bitmask == 0x100) {
                bitmap++;
                bitmask = 1;
            }
        }
    }

    *isNull = resultnull;
    return result;
}

/* ----------------------------------------------------------------
 *		ExecEvalNot
 *		ExecEvalOr
 *		ExecEvalAnd
 *
 *		Evaluate boolean expressions, with appropriate short-circuiting.
 *
 *		The query planner reformulates clause expressions in the
 *		qualification to conjunctive normal form.  If we ever get
 *		an AND to evaluate, we can be sure that it's not a top-level
 *		clause in the qualification, but appears lower (as a function
 *		argument, for example), or in the target list.	Not that you
 *		need to know this, mind you...
 * ----------------------------------------------------------------
 */
static Datum ExecEvalNot(BoolExprState* notclause, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    ExprState* clause = (ExprState*)linitial(notclause->args);
    Datum expr_value;

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    expr_value = ExecEvalExpr(clause, econtext, isNull, NULL);

    /*
     * if the expression evaluates to null, then we just cascade the null back
     * to whoever called us.
     */
    if (*isNull)
        return expr_value;

    /*
     * evaluation of 'not' is simple.. expr is false, then return 'true' and
     * vice versa.
     */
    return BoolGetDatum(!DatumGetBool(expr_value));
}

/* ----------------------------------------------------------------
 *		ExecEvalOr
 * ----------------------------------------------------------------
 */
static Datum ExecEvalOr(BoolExprState* orExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    List* clauses = orExpr->args;
    ListCell* clause = NULL;
    bool AnyNull = false;

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    AnyNull = false;

    /*
     * If any of the clauses is TRUE, the OR result is TRUE regardless of the
     * states of the rest of the clauses, so we can stop evaluating and return
     * TRUE immediately.  If none are TRUE and one or more is NULL, we return
     * NULL; otherwise we return FALSE.  This makes sense when you interpret
     * NULL as "don't know": if we have a TRUE then the OR is TRUE even if we
     * aren't sure about some of the other inputs. If all the known inputs are
     * FALSE, but we have one or more "don't knows", then we have to report
     * that we "don't know" what the OR's result should be --- perhaps one of
     * the "don't knows" would have been TRUE if we'd known its value.  Only
     * when all the inputs are known to be FALSE can we state confidently that
     * the OR's result is FALSE.
     */
    foreach (clause, clauses) {
        ExprState* clausestate = (ExprState*)lfirst(clause);
        Datum clause_value;

        clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL);

        /*
         * if we have a non-null true result, then return it.
         */
        if (*isNull)
            AnyNull = true; /* remember we got a null */
        else if (DatumGetBool(clause_value))
            return clause_value;
    }

    /* AnyNull is true if at least one clause evaluated to NULL */
    *isNull = AnyNull;
    return BoolGetDatum(false);
}

/* ----------------------------------------------------------------
 *		ExecEvalAnd
 * ----------------------------------------------------------------
 */
static Datum ExecEvalAnd(BoolExprState* andExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    List* clauses = andExpr->args;
    ListCell* clause = NULL;
    bool AnyNull = false;

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    AnyNull = false;

    /*
     * If any of the clauses is FALSE, the AND result is FALSE regardless of
     * the states of the rest of the clauses, so we can stop evaluating and
     * return FALSE immediately.  If none are FALSE and one or more is NULL,
     * we return NULL; otherwise we return TRUE.  This makes sense when you
     * interpret NULL as "don't know", using the same sort of reasoning as for
     * OR, above.
     */
    foreach (clause, clauses) {
        ExprState* clausestate = (ExprState*)lfirst(clause);
        Datum clause_value;

        clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL);

        /*
         * if we have a non-null false result, then return it.
         */
        if (*isNull)
            AnyNull = true; /* remember we got a null */
        else if (!DatumGetBool(clause_value))
            return clause_value;
    }

    /* AnyNull is true if at least one clause evaluated to NULL */
    *isNull = AnyNull;
    return BoolGetDatum(!AnyNull);
}

/* ----------------------------------------------------------------
 *		ExecEvalConvertRowtype
 *
 *		Evaluate a rowtype coercion operation.	This may require
 *		rearranging field positions.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalConvertRowtype(
    ConvertRowtypeExprState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    ConvertRowtypeExpr* convert = (ConvertRowtypeExpr*)cstate->xprstate.expr;
    HeapTuple result;
    Datum tupDatum;
    HeapTupleHeader tuple;
    HeapTupleData tmptup;

    tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);

    /* this test covers the isDone exception too: */
    if (*isNull)
        return tupDatum;

    tuple = DatumGetHeapTupleHeader(tupDatum);

    /* Lookup tupdescs if first time through or after rescan */
    if (cstate->indesc == NULL) {
        get_cached_rowtype(exprType((Node*)convert->arg), -1, &cstate->indesc, econtext);
        cstate->initialized = false;
    }
    if (cstate->outdesc == NULL) {
        get_cached_rowtype(convert->resulttype, -1, &cstate->outdesc, econtext);
        cstate->initialized = false;
    }

    Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid);
    Assert(HeapTupleHeaderGetTypMod(tuple) == cstate->indesc->tdtypmod);

    /* if first time through, initialize conversion map */
    if (!cstate->initialized) {
        MemoryContext old_cxt;

        /* allocate map in long-lived memory context */
        old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);

        /* prepare map from old to new attribute numbers */
        cstate->map =
            convert_tuples_by_name(cstate->indesc, cstate->outdesc, gettext_noop("could not convert row type"));
        cstate->initialized = true;

        MemoryContextSwitchTo(old_cxt);
    }

    /*
     * No-op if no conversion needed (not clear this can happen here).
     */
    if (cstate->map == NULL)
        return tupDatum;

    /*
     * do_convert_tuple needs a HeapTuple not a bare HeapTupleHeader.
     */
    tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
    tmptup.t_data = tuple;

    result = do_convert_tuple(&tmptup, cstate->map);

    return HeapTupleGetDatum(result);
}

/* ----------------------------------------------------------------
 *		ExecEvalCase
 *
 *		Evaluate a CASE clause. Will have boolean expressions
 *		inside the WHEN clauses, and will have expressions
 *		for results.
 *		- thomas 1998-11-09
 * ----------------------------------------------------------------
 */
static Datum ExecEvalCase(CaseExprState* caseExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    List* clauses = caseExpr->args;
    ListCell* clause = NULL;
    Datum save_datum;
    bool save_isNull = false;

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    /*
     * If there's a test expression, we have to evaluate it and save the value
     * where the CaseTestExpr placeholders can find it. We must save and
     * restore prior setting of econtext's caseValue fields, in case this node
     * is itself within a larger CASE.Furthermore, don't assign to the
     * econtext fields until after returning from evaluation of the test
     * expression.  We used to pass &econtext->caseValue_isNull to the
     * recursive call, but that leads to aliasing that variable within said
     * call, which can (and did) produce bugs when the test expression itself
     * contains a CASE.
     *
     * If there's no test expression, we don't actually need to save and
     * restore these fields; but it's less code to just do so unconditionally.
     */
    save_datum = econtext->caseValue_datum;
    save_isNull = econtext->caseValue_isNull;

    if (caseExpr->arg) {
        bool arg_isNull = false;
        econtext->caseValue_datum = ExecEvalExpr(caseExpr->arg, econtext, &arg_isNull, NULL);
        econtext->caseValue_isNull = arg_isNull;
    }

    /*
     * we evaluate each of the WHEN clauses in turn, as soon as one is true we
     * return the corresponding result. If none are true then we return the
     * value of the default clause, or NULL if there is none.
     */
    foreach (clause, clauses) {
        CaseWhenState* wclause = (CaseWhenState*)lfirst(clause);
        Datum clause_value;
        bool clause_isNull = false;

        clause_value = ExecEvalExpr(wclause->expr, econtext, &clause_isNull, NULL);

        /*
         * if we have a true test, then we return the result, since the case
         * statement is satisfied.	A NULL result from the test is not
         * considered true.
         */
        if (DatumGetBool(clause_value) && !clause_isNull) {
            econtext->caseValue_datum = save_datum;
            econtext->caseValue_isNull = save_isNull;
            return ExecEvalExpr(wclause->result, econtext, isNull, isDone);
        }
    }

    econtext->caseValue_datum = save_datum;
    econtext->caseValue_isNull = save_isNull;

    if (caseExpr->defresult) {
        return ExecEvalExpr(caseExpr->defresult, econtext, isNull, isDone);
    }

    *isNull = true;
    return (Datum)0;
}

/*
 * ExecEvalCaseTestExpr
 *
 * Return the value stored by CASE.
 */
static Datum ExecEvalCaseTestExpr(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    if (isDone != NULL)
        *isDone = ExprSingleResult;
    *isNull = econtext->caseValue_isNull;
    return econtext->caseValue_datum;
}

/*
 * ExecEvalGroupingFuncExpr
 *
 * Return a bitmask with a bit for each (unevaluated) argument expression
 * (rightmost arg is least significant bit).
 *
 * A bit is set if the corresponding expression is NOT part of the set of
 * grouping expressions in the current grouping set.
 */
static Datum ExecEvalGroupingFuncExpr(
    GroupingFuncExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    int result = 0;
    int attnum = 0;
    Bitmapset* grouped_cols = gstate->aggstate->grouped_cols;
    ListCell* lc = NULL;

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    *isNull = false;

    foreach (lc, (gstate->clauses)) {
        attnum = lfirst_int(lc);

        result = (uint32)result << 1;

        if (!bms_is_member(attnum, grouped_cols))
            result = (uint32)result | 1;
    }

    return (Datum)result;
}

/* ----------------------------------------------------------------
 *		ExecEvalArray - ARRAY[] expressions
 * ----------------------------------------------------------------
 */
static Datum ExecEvalArray(ArrayExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    ArrayExpr* arrayExpr = (ArrayExpr*)astate->xprstate.expr;
    ArrayType* result = NULL;
    ListCell* element = NULL;
    Oid element_type = arrayExpr->element_typeid;
    int ndims = 0;
    int dims[MAXDIM];
    int lbs[MAXDIM];

    /* Set default values for result flags: non-null, not a set result */
    *isNull = false;
    if (isDone != NULL)
        *isDone = ExprSingleResult;

    if (!arrayExpr->multidims) {
        /* Elements are presumably of scalar type */
        int nelems;
        Datum* dvalues = NULL;
        bool* dnulls = NULL;
        int i = 0;

        ndims = 1;
        nelems = list_length(astate->elements);

        /* Shouldn't happen here, but if length is 0, return empty array */
        if (nelems == 0)
            return PointerGetDatum(construct_empty_array(element_type));

        dvalues = (Datum*)palloc(nelems * sizeof(Datum));
        dnulls = (bool*)palloc(nelems * sizeof(bool));

        /* loop through and build array of datums */
        foreach (element, astate->elements) {
            ExprState* e = (ExprState*)lfirst(element);

            dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i], NULL);
            i++;
        }

        /* setup for 1-D array of the given length */
        dims[0] = nelems;
        lbs[0] = 1;

        result = construct_md_array(
            dvalues, dnulls, ndims, dims, lbs, element_type, astate->elemlength, astate->elembyval, astate->elemalign);
    } else {
        /* Must be nested array expressions */
        int nbytes = 0;
        int nitems = 0;
        int outer_nelems = 0;
        int elem_ndims = 0;
        int* elem_dims = NULL;
        int* elem_lbs = NULL;
        bool firstone = true;
        bool havenulls = false;
        bool haveempty = false;
        char** subdata;
        bits8** subbitmaps;
        int* subbytes = NULL;
        int* subnitems = NULL;
        int i;
        int32 dataoffset;
        char* dat = NULL;
        int iitem;
        errno_t rc = 0;

        i = list_length(astate->elements);
        subdata = (char**)palloc(i * sizeof(char*));
        subbitmaps = (bits8**)palloc(i * sizeof(bits8*));
        subbytes = (int*)palloc(i * sizeof(int));
        subnitems = (int*)palloc(i * sizeof(int));

        /* loop through and get data area from each element */
        foreach (element, astate->elements) {
            ExprState* e = (ExprState*)lfirst(element);
            bool eisnull = false;
            Datum arraydatum;
            ArrayType* array = NULL;
            int this_ndims;

            arraydatum = ExecEvalExpr(e, econtext, &eisnull, NULL);
            /* temporarily ignore null subarrays */
            if (eisnull) {
                haveempty = true;
                continue;
            }

            array = DatumGetArrayTypeP(arraydatum);

            /* run-time double-check on element type */
            if (element_type != ARR_ELEMTYPE(array))
                ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                        errmsg("cannot merge incompatible arrays"),
                        errdetail("Array with element type %s cannot be "
                                  "included in ARRAY construct with element type %s.",
                            format_type_be(ARR_ELEMTYPE(array)),
                            format_type_be(element_type))));

            this_ndims = ARR_NDIM(array);
            /* temporarily ignore zero-dimensional subarrays */
            if (this_ndims <= 0) {
                haveempty = true;
                continue;
            }

            if (firstone) {
                /* Get sub-array details from first member */
                elem_ndims = this_ndims;
                ndims = elem_ndims + 1;
                if (ndims <= 0 || ndims > MAXDIM)
                    ereport(ERROR,
                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                            errmsg("number of array dimensions (%d) exceeds "
                                   "the maximum allowed (%d)",
                                ndims,
                                MAXDIM)));

                elem_dims = (int*)palloc(elem_ndims * sizeof(int));
                rc = memcpy_s(elem_dims, elem_ndims * sizeof(int), ARR_DIMS(array), elem_ndims * sizeof(int));
                securec_check(rc, "\0", "\0");

                elem_lbs = (int*)palloc(elem_ndims * sizeof(int));
                rc = memcpy_s(elem_lbs, elem_ndims * sizeof(int), ARR_LBOUND(array), elem_ndims * sizeof(int));
                securec_check(rc, "\0", "\0");

                firstone = false;
            } else {
                /* Check other sub-arrays are compatible */
                if (elem_ndims != this_ndims || memcmp(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int)) != 0 ||
                    memcmp(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int)) != 0)
                    ereport(ERROR,
                        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
                            errmsg("multidimensional arrays must have array expressions with matching dimensions")));
            }

            subdata[outer_nelems] = ARR_DATA_PTR(array);
            subbitmaps[outer_nelems] = ARR_NULLBITMAP(array);
            subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array);
            nbytes += subbytes[outer_nelems];
            subnitems[outer_nelems] = ArrayGetNItems(this_ndims, ARR_DIMS(array));
            nitems += subnitems[outer_nelems];
            if (ARR_HASNULL(array))
                havenulls = true;
            outer_nelems++;
        }

        /*
         * If all items were null or empty arrays, return an empty array;
         * otherwise, if some were and some weren't, raise error.  (Note: we
         * must special-case this somehow to avoid trying to generate a 1-D
         * array formed from empty arrays.	It's not ideal...)
         */
        if (haveempty) {
            if (ndims == 0) /* didn't find any nonempty array */
                return PointerGetDatum(construct_empty_array(element_type));
            ereport(ERROR,
                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
                    errmsg("multidimensional arrays must have array expressions with matching dimensions")));
        }

        /* setup for multi-D array */
        dims[0] = outer_nelems;
        lbs[0] = 1;
        for (i = 1; i < ndims; i++) {
            dims[i] = elem_dims[i - 1];
            lbs[i] = elem_lbs[i - 1];
        }

        if (havenulls) {
            dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
            nbytes += dataoffset;
        } else {
            dataoffset = 0; /* marker for no null bitmap */
            nbytes += ARR_OVERHEAD_NONULLS(ndims);
        }

        result = (ArrayType*)palloc(nbytes);
        SET_VARSIZE(result, nbytes);
        result->ndim = ndims;
        result->dataoffset = dataoffset;
        result->elemtype = element_type;
        rc = memcpy_s(ARR_DIMS(result), ndims * sizeof(int), dims, ndims * sizeof(int));
        securec_check(rc, "\0", "\0");
        rc = memcpy_s(ARR_LBOUND(result), ndims * sizeof(int), lbs, ndims * sizeof(int));
        securec_check(rc, "\0", "\0");

        dat = ARR_DATA_PTR(result);

        int len = (nbytes - ARR_DATA_OFFSET(result));
        iitem = 0;
        for (i = 0; i < outer_nelems; i++) {
            /* make sure the destMax of memcpy_s should never be zero. */
            if (subbytes[i] != 0) {
                rc = memcpy_s(dat, len, subdata[i], subbytes[i]);
                securec_check(rc, "\0", "\0");
            }

            dat += subbytes[i];
            len -= subbytes[i];
            if (havenulls)
                array_bitmap_copy(ARR_NULLBITMAP(result), iitem, subbitmaps[i], 0, subnitems[i]);
            iitem += subnitems[i];
        }
    }

    return PointerGetDatum(result);
}

/* ----------------------------------------------------------------
 *		ExecEvalRow - ROW() expressions
 * ----------------------------------------------------------------
 */
static Datum ExecEvalRow(RowExprState* rstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    HeapTuple tuple;
    Datum* values = NULL;
    bool* isnull = NULL;
    int natts;
    ListCell* arg = NULL;
    int i;
    errno_t rc = EOK;

    /* Set default values for result flags: non-null, not a set result */
    *isNull = false;
    if (isDone != NULL)
        *isDone = ExprSingleResult;

    /* Allocate workspace */
    natts = rstate->tupdesc->natts;
    values = (Datum*)palloc0(natts * sizeof(Datum));
    isnull = (bool*)palloc(natts * sizeof(bool));

    /* preset to nulls in case rowtype has some later-added columns */
    rc = memset_s(isnull, natts * sizeof(bool), true, natts * sizeof(bool));
    securec_check(rc, "\0", "\0");

    /* Evaluate field values */
    i = 0;
    foreach (arg, rstate->args) {
        ExprState* e = (ExprState*)lfirst(arg);

        values[i] = ExecEvalExpr(e, econtext, &isnull[i], NULL);
        i++;
    }

    tuple = (HeapTuple)tableam_tops_form_tuple(rstate->tupdesc, values, isnull, HEAP_TUPLE);

    pfree_ext(values);
    pfree_ext(isnull);

    return HeapTupleGetDatum(tuple);
}

/* ----------------------------------------------------------------
 *		ExecEvalRowCompare - ROW() comparison-op ROW()
 * ----------------------------------------------------------------
 */
static Datum ExecEvalRowCompare(RowCompareExprState* rstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    bool result = false;
    RowCompareType rctype = ((RowCompareExpr*)rstate->xprstate.expr)->rctype;
    int32 cmpresult = 0;
    ListCell* l = NULL;
    ListCell* r = NULL;
    int i;

    if (isDone != NULL)
        *isDone = ExprSingleResult;
    *isNull = true; /* until we get a result */

    i = 0;
    forboth(l, rstate->largs, r, rstate->rargs)
    {
        ExprState* le = (ExprState*)lfirst(l);
        ExprState* re = (ExprState*)lfirst(r);
        FunctionCallInfoData locfcinfo;

        InitFunctionCallInfoData(locfcinfo, &(rstate->funcs[i]), 2, rstate->collations[i], NULL, NULL);
        locfcinfo.arg[0] = ExecEvalExpr(le, econtext, &locfcinfo.argnull[0], NULL);
        locfcinfo.arg[1] = ExecEvalExpr(re, econtext, &locfcinfo.argnull[1], NULL);
        if (rstate->funcs[i].fn_strict && (locfcinfo.argnull[0] || locfcinfo.argnull[1]))
            return (Datum)0; /* force NULL result */
        locfcinfo.isnull = false;
        cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
        if (locfcinfo.isnull)
            return (Datum)0; /* force NULL result */
        if (cmpresult != 0)
            break; /* no need to compare remaining columns */
        i++;
    }

    switch (rctype) {
            /* EQ and NE cases aren't allowed here */
        case ROWCOMPARE_LT:
            result = (cmpresult < 0);
            break;
        case ROWCOMPARE_LE:
            result = (cmpresult <= 0);
            break;
        case ROWCOMPARE_GE:
            result = (cmpresult >= 0);
            break;
        case ROWCOMPARE_GT:
            result = (cmpresult > 0);
            break;
        default:
            ereport(ERROR,
                (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                    errmodule(MOD_EXECUTOR),
                    errmsg("unrecognized RowCompareType: %d", (int)rctype)));
            result = 0; /* keep compiler quiet */
            break;
    }

    *isNull = false;
    return BoolGetDatum(result);
}

/* ----------------------------------------------------------------
 *		ExecEvalCoalesce
 * ----------------------------------------------------------------
 */
static Datum ExecEvalCoalesce(
    CoalesceExprState* coalesceExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    ListCell* arg = NULL;

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    /* Simply loop through until something NOT NULL is found */
    foreach (arg, coalesceExpr->args) {
        ExprState* e = (ExprState*)lfirst(arg);
        Datum value;

        value = ExecEvalExpr(e, econtext, isNull, NULL);
        if (!*isNull)
            return value;
    }

    /* Else return NULL */
    *isNull = true;
    return (Datum)0;
}

/* ----------------------------------------------------------------
 *		ExecEvalMinMax
 * ----------------------------------------------------------------
 */
static Datum ExecEvalMinMax(MinMaxExprState* minmaxExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    Datum result = (Datum)0;
    MinMaxExpr* minmax = (MinMaxExpr*)minmaxExpr->xprstate.expr;
    Oid collation = minmax->inputcollid;
    MinMaxOp op = minmax->op;
    FunctionCallInfoData locfcinfo;
    ListCell* arg = NULL;

    if (isDone != NULL)
        *isDone = ExprSingleResult;
    *isNull = true; /* until we get a result */

    InitFunctionCallInfoData(locfcinfo, &minmaxExpr->cfunc, 2, collation, NULL, NULL);
    locfcinfo.argnull[0] = false;
    locfcinfo.argnull[1] = false;

    foreach (arg, minmaxExpr->args) {
        ExprState* e = (ExprState*)lfirst(arg);
        Datum value;
        bool valueIsNull = false;
        int32 cmpresult;

        value = ExecEvalExpr(e, econtext, &valueIsNull, NULL);
        if (valueIsNull) {
#ifdef DOLPHIN
            *isNull = true;
            return (Datum)0; /* return NULL if inputs include NULL for B format*/
#else
            continue; /* ignore NULL inputs */
#endif
        }

        if (*isNull) {
            /* first nonnull input, adopt value */
            result = value;
            *isNull = false;
        } else {
            /* apply comparison function */
            locfcinfo.arg[0] = result;
            locfcinfo.arg[1] = value;
            locfcinfo.isnull = false;
            cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
            if (locfcinfo.isnull) /* probably should not happen */
                continue;
            if (cmpresult > 0 && op == IS_LEAST)
                result = value;
            else if (cmpresult < 0 && op == IS_GREATEST)
                result = value;
        }
    }

    return result;
}

/* ----------------------------------------------------------------
 *		ExecEvalXml
 * ----------------------------------------------------------------
 */
static Datum ExecEvalXml(XmlExprState* xmlExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    XmlExpr* xexpr = (XmlExpr*)xmlExpr->xprstate.expr;
    Datum value;
    bool isnull = false;
    ListCell* arg = NULL;
    ListCell* narg = NULL;

    if (isDone != NULL)
        *isDone = ExprSingleResult;
    *isNull = true; /* until we get a result */

    switch (xexpr->op) {
        case IS_XMLCONCAT: {
            List* values = NIL;

            foreach (arg, xmlExpr->args) {
                ExprState* e = (ExprState*)lfirst(arg);

                value = ExecEvalExpr(e, econtext, &isnull, NULL);
                if (!isnull)
                    values = lappend(values, DatumGetPointer(value));
            }

            if (list_length(values) > 0) {
                *isNull = false;
                return PointerGetDatum(xmlconcat(values));
            } else
                return (Datum)0;
        } break;

        case IS_XMLFOREST: {
            StringInfoData buf;

            initStringInfo(&buf);
            forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names)
            {
                ExprState* e = (ExprState*)lfirst(arg);
                char* argname = strVal(lfirst(narg));

                value = ExecEvalExpr(e, econtext, &isnull, NULL);
                if (!isnull) {
                    appendStringInfo(&buf,
                        "<%s>%s</%s>",
                        argname,
                        map_sql_value_to_xml_value(value, exprType((Node*)e->expr), true),
                        argname);
                    *isNull = false;
                }
            }

            if (*isNull) {
                pfree_ext(buf.data);
                return (Datum)0;
            } else {
                text* result = NULL;

                result = cstring_to_text_with_len(buf.data, buf.len);
                pfree_ext(buf.data);

                return PointerGetDatum(result);
            }
        } break;

        case IS_XMLELEMENT:
            *isNull = false;
            return PointerGetDatum(xmlelement(xmlExpr, econtext));
            break;

        case IS_XMLPARSE: {
            ExprState* e = NULL;
            text* data = NULL;
            bool preserve_whitespace = false;

            /* arguments are known to be text, bool */
            Assert(list_length(xmlExpr->args) == 2);

            e = (ExprState*)linitial(xmlExpr->args);
            value = ExecEvalExpr(e, econtext, &isnull, NULL);
            if (isnull)
                return (Datum)0;
            data = DatumGetTextP(value);

            e = (ExprState*)lsecond(xmlExpr->args);
            value = ExecEvalExpr(e, econtext, &isnull, NULL);
            if (isnull) /* probably can't happen */
                return (Datum)0;
            preserve_whitespace = DatumGetBool(value);

            *isNull = false;

            return PointerGetDatum(xmlparse(data, xexpr->xmloption, preserve_whitespace));
        } break;

        case IS_XMLPI: {
            ExprState* e = NULL;
            text* argument = NULL;

            /* optional argument is known to be text */
            Assert(list_length(xmlExpr->args) <= 1);

            if (xmlExpr->args) {
                e = (ExprState*)linitial(xmlExpr->args);
                value = ExecEvalExpr(e, econtext, &isnull, NULL);
                if (isnull)
                    argument = NULL;
                else
                    argument = DatumGetTextP(value);
            } else {
                argument = NULL;
                isnull = false;
            }

            return PointerGetDatum(xmlpi(xexpr->name, argument, isnull, isNull));
        } break;

        case IS_XMLROOT: {
            ExprState* e = NULL;
            xmltype* data = NULL;
            text* version = NULL;
            int standalone;

            /* arguments are known to be xml, text, int */
            Assert(list_length(xmlExpr->args) == 3);

            e = (ExprState*)linitial(xmlExpr->args);
            value = ExecEvalExpr(e, econtext, &isnull, NULL);
            if (isnull)
                return (Datum)0;
            data = DatumGetXmlP(value);

            e = (ExprState*)lsecond(xmlExpr->args);
            value = ExecEvalExpr(e, econtext, &isnull, NULL);
            if (isnull)
                version = NULL;
            else
                version = DatumGetTextP(value);

            e = (ExprState*)lthird(xmlExpr->args);
            value = ExecEvalExpr(e, econtext, &isnull, NULL);
            standalone = DatumGetInt32(value);

            *isNull = false;

            return PointerGetDatum(xmlroot(data, version, standalone));
        } break;

        case IS_XMLSERIALIZE: {
            ExprState* e = NULL;

            /* argument type is known to be xml */
            Assert(list_length(xmlExpr->args) == 1);

            e = (ExprState*)linitial(xmlExpr->args);
            value = ExecEvalExpr(e, econtext, &isnull, NULL);
            if (isnull)
                return (Datum)0;

            *isNull = false;

            return PointerGetDatum(xmltotext_with_xmloption(DatumGetXmlP(value), xexpr->xmloption));
        } break;

        case IS_DOCUMENT: {
            ExprState* e = NULL;

            /* optional argument is known to be xml */
            Assert(list_length(xmlExpr->args) == 1);

            e = (ExprState*)linitial(xmlExpr->args);
            value = ExecEvalExpr(e, econtext, &isnull, NULL);
            if (isnull)
                return (Datum)0;
            else {
                *isNull = false;
                return BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
            }
        } break;
        default:
            break;
    }

    ereport(ERROR,
        (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
            errmodule(MOD_EXECUTOR),
            errmsg("unrecognized XML operation %d", xexpr->op)));
    return (Datum)0;
}

/* ----------------------------------------------------------------
 *		ExecEvalNullIf
 *
 * Note that this is *always* derived from the equals operator,
 * but since we need special processing of the arguments
 * we can not simply reuse ExecEvalOper() or ExecEvalFunc().
 * ----------------------------------------------------------------
 */
static Datum ExecEvalNullIf(FuncExprState* nullIfExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    Datum result;
    FunctionCallInfo fcinfo;
    ExprDoneCond argDone;

    if (isDone != NULL)
        *isDone = ExprSingleResult;

    /*
     * Initialize function cache if first time through
     */
    if (nullIfExpr->func.fn_oid == InvalidOid) {
        NullIfExpr* op = (NullIfExpr*)nullIfExpr->xprstate.expr;

        init_fcache<false>(op->opfuncid, op->inputcollid, nullIfExpr, econtext->ecxt_per_query_memory, true);
        Assert(!nullIfExpr->func.fn_retset);
    }

    /*
     * Evaluate arguments
     */
    fcinfo = &nullIfExpr->fcinfo_data;
    argDone = ExecEvalFuncArgs<false>(fcinfo, nullIfExpr->args, econtext);
    if (argDone != ExprSingleResult)
        ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("NULLIF does not support set arguments")));
    Assert(fcinfo->nargs == 2);

    /* if either argument is NULL they can't be equal */
    if (!fcinfo->argnull[0] && !fcinfo->argnull[1]) {
        fcinfo->isnull = false;
        result = FunctionCallInvoke(fcinfo);
        /* if the arguments are equal return null */
        if (!fcinfo->isnull && DatumGetBool(result)) {
            *isNull = true;
            return (Datum)0;
        }
    }

    /* else return first argument */
    *isNull = fcinfo->argnull[0];
    return fcinfo->arg[0];
}

static Datum CheckRowTypeIsNull(TupleDesc tupDesc, HeapTupleData tmptup, NullTest *ntest)
{
    int att;

    for (att = 1; att <= tupDesc->natts; att++) {
        /* ignore dropped columns */
        if (tupDesc->attrs[att - 1]->attisdropped)
            continue;
        if (tableam_tops_tuple_attisnull(&tmptup, att, tupDesc)) {
            /* null field disproves IS NOT NULL */
            if (ntest->nulltesttype == IS_NOT_NULL)
                return BoolGetDatum(false);
        } else {
            /* non-null field disproves IS NULL */
            if (ntest->nulltesttype == IS_NULL)
                return BoolGetDatum(false);
        }
    }

    return BoolGetDatum(true);
}

static Datum CheckRowTypeIsNullForAFormat(TupleDesc tupDesc, HeapTupleData tmptup, NullTest *ntest)
{
    int att;

    for (att = 1; att <= tupDesc->natts; att++) {
        /* ignore dropped columns */
        if (tupDesc->attrs[att - 1]->attisdropped)
            continue;
        if (!tableam_tops_tuple_attisnull(&tmptup, att, tupDesc)) {
            /* non-null field disproves IS NULL */
            if (ntest->nulltesttype == IS_NULL) {
                return BoolGetDatum(false);
            } else {
                return BoolGetDatum(true);
            }
        }
    }

    /* non-null field disproves IS NULL */
    if (ntest->nulltesttype == IS_NULL) {
        return BoolGetDatum(true);
    } else {
        return BoolGetDatum(false);
    }
}

/* ----------------------------------------------------------------
 *		ExecEvalNullTest
 *
 *		Evaluate a NullTest node.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalNullTest(NullTestState* nstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    NullTest* ntest = (NullTest*)nstate->xprstate.expr;
    Datum result;

    result = ExecEvalExpr(nstate->arg, econtext, isNull, isDone);

    if (isDone && *isDone == ExprEndResult)
        return result; /* nothing to check */

    if (ntest->argisrow && !(*isNull)) {
        /*
         * The SQL standard defines IS [NOT] NULL for a non-null rowtype
         * argument as:
         *
         * "R IS NULL" is true if every field is the null value.
         *
         * "R IS NOT NULL" is true if no field is the null value.
         *
         * This definition is (apparently intentionally) not recursive; so our
         * tests on the fields are primitive attisnull tests, not recursive
         * checks to see if they are all-nulls or no-nulls rowtypes.
         *
         * The standard does not consider the possibility of zero-field rows,
         * but here we consider them to vacuously satisfy both predicates.
         * 
         * e.g.
         *            r      | isnull | isnotnull 
         *      -------------+--------+-----------
         *       (1,"(1,2)") | f      | t
         *       (1,"(,)")   | f      | t
         *       (1,)        | f      | f
         *       (,"(1,2)")  | f      | f
         *       (,"(,)")    | f      | f
         *       (,)         | t      | f
         * 
         */
        HeapTupleHeader tuple;
        Oid tupType;
        int32 tupTypmod;
        TupleDesc tupDesc;
        HeapTupleData tmptup;

        tuple = DatumGetHeapTupleHeader(result);

        tupType = HeapTupleHeaderGetTypeId(tuple);
        tupTypmod = HeapTupleHeaderGetTypMod(tuple);

        /* Lookup tupdesc if first time through or if type changes */
        tupDesc = get_cached_rowtype(tupType, tupTypmod, &nstate->argdesc, econtext);

        /*
         * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
         */
        tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
        tmptup.t_data = tuple;

        if (AFORMAT_NULL_TEST_MODE) {
            return CheckRowTypeIsNullForAFormat(tupDesc, tmptup, ntest);
        } else {
            return CheckRowTypeIsNull(tupDesc, tmptup, ntest);
        }
    } else {
        /* Simple scalar-argument case, or a null rowtype datum */
        switch (ntest->nulltesttype) {
            case IS_NULL:
                if (*isNull) {
                    *isNull = false;
                    return BoolGetDatum(true);
                } else
                    return BoolGetDatum(false);
            case IS_NOT_NULL:
                if (*isNull) {
                    *isNull = false;
                    return BoolGetDatum(false);
                } else
                    return BoolGetDatum(true);
            default:
                ereport(ERROR,
                    (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                        errmodule(MOD_EXECUTOR),
                        errmsg("unrecognized nulltesttype: %d", (int)ntest->nulltesttype)));
                return (Datum)0; /* keep compiler quiet */
        }
    }
}

/* ----------------------------------------------------------------
 *		ExecEvalHashFilter
 *
 *		Evaluate a HashFilter node.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalHashFilter(HashFilterState* hstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    HashFilter* htest = (HashFilter*)hstate->xprstate.expr;
    Datum result = 0;
    Datum value = 0;
    uint64 hashValue = 0;
    int modulo = 0;
    int nodeIndex = 0;
    ListCell *distkey = NULL;
    ListCell *vartypes = NULL;
    bool isFirst = true;
    bool hasNonNullValue = false;

    if (isDone != NULL)
        *isDone = ExprSingleResult;
    *isNull = true; /* until we get a result */

    /* Get every distribute key in arg and compute hash value */
    forboth(distkey, hstate->arg, vartypes, htest->typeOids)
    {
        ExprState* e = (ExprState*)lfirst(distkey);
        Oid vartype = (Oid)lfirst_oid(vartypes);
        value = ExecEvalExpr(e, econtext, isNull, isDone);

        int null_value_dn_index = (hstate->nodelist != NULL) ? hstate->nodelist[0]
                                                             : /* fetch first dn in group's dn list */
                                      0;                       /* fetch first dn index */

        if (*isNull) {
            if (null_value_dn_index == u_sess->pgxc_cxt.PGXCNodeId) {
                *isNull = false;
                result = BoolGetDatum(true);
            } else
                result = BoolGetDatum(false);
        } else {
            if (isFirst) {
                hashValue = compute_hash(vartype, value, LOCATOR_TYPE_HASH);
                isFirst = false;
            } else {
                hashValue = (hashValue << 1) | ((hashValue & 0x80000000) ? 1 : 0);
                hashValue ^= compute_hash(vartype, value, LOCATOR_TYPE_HASH);
            }

            hasNonNullValue = true;
        }
    }

    /* If has non null value, it should get nodeId and deside if need filter the value or not. */
    if (hasNonNullValue) {
        modulo = hstate->bucketMap[abs((int)hashValue) & (hstate->bucketCnt - 1)];
        nodeIndex = hstate->nodelist[modulo];

        /* If there are null value and non null value, and the last value in distkey is null,
            we should set isNull is false. */
        *isNull = false;
        /* Look into the handles and return correct position in array */
        if (nodeIndex == u_sess->pgxc_cxt.PGXCNodeId)
            return BoolGetDatum(true);
        else
            return BoolGetDatum(false);
    } else /* If all the value is null, return result. */
        return result;
}

/* ----------------------------------------------------------------
 *		ExecEvalBooleanTest
 *
 *		Evaluate a BooleanTest node.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalBooleanTest(GenericExprState* bstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    BooleanTest* btest = (BooleanTest*)bstate->xprstate.expr;
    Datum result;

    result = ExecEvalExpr(bstate->arg, econtext, isNull, isDone);

    if (isDone && *isDone == ExprEndResult)
        return result; /* nothing to check */

    switch (btest->booltesttype) {
        case IS_TRUE:
            if (*isNull) {
                *isNull = false;
                return BoolGetDatum(false);
            } else if (DatumGetBool(result))
                return BoolGetDatum(true);
            else
                return BoolGetDatum(false);
        case IS_NOT_TRUE:
            if (*isNull) {
                *isNull = false;
                return BoolGetDatum(true);
            } else if (DatumGetBool(result))
                return BoolGetDatum(false);
            else
                return BoolGetDatum(true);
        case IS_FALSE:
            if (*isNull) {
                *isNull = false;
                return BoolGetDatum(false);
            } else if (DatumGetBool(result))
                return BoolGetDatum(false);
            else
                return BoolGetDatum(true);
        case IS_NOT_FALSE:
            if (*isNull) {
                *isNull = false;
                return BoolGetDatum(true);
            } else if (DatumGetBool(result))
                return BoolGetDatum(true);
            else
                return BoolGetDatum(false);
        case IS_UNKNOWN:
            if (*isNull) {
                *isNull = false;
                return BoolGetDatum(true);
            } else
                return BoolGetDatum(false);
        case IS_NOT_UNKNOWN:
            if (*isNull) {
                *isNull = false;
                return BoolGetDatum(false);
            } else
                return BoolGetDatum(true);
        default:
            ereport(ERROR,
                (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                    errmodule(MOD_EXECUTOR),
                    errmsg("unrecognized booltesttype: %d", (int)btest->booltesttype)));
            return (Datum)0; /* keep compiler quiet */
    }
}

/*
 * ExecEvalCoerceToDomain
 *
 * Test the provided data against the domain constraint(s).  If the data
 * passes the constraint specifications, pass it through (return the
 * datum) otherwise throw an error.
 */
static Datum ExecEvalCoerceToDomain(
    CoerceToDomainState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    CoerceToDomain* ctest = (CoerceToDomain*)cstate->xprstate.expr;
    Datum result;
    ListCell* l = NULL;

    result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);

    if (isDone && *isDone == ExprEndResult)
        return result; /* nothing to check */

    foreach (l, cstate->constraints) {
        DomainConstraintState* con = (DomainConstraintState*)lfirst(l);

        switch (con->constrainttype) {
            case DOM_CONSTRAINT_NOTNULL:
                if (*isNull)
                    ereport(ERROR,
                        (errcode(ERRCODE_NOT_NULL_VIOLATION),
                            errmsg("domain %s does not allow null values", format_type_be(ctest->resulttype))));
                break;
            case DOM_CONSTRAINT_CHECK: {
                Datum conResult;
                bool conIsNull = false;
                Datum save_datum;
                bool save_isNull = false;

                /*
                 * Set up value to be returned by CoerceToDomainValue
                 * nodes. We must save and restore prior setting of
                 * econtext's domainValue fields, in case this node is
                 * itself within a check expression for another domain.
                 */
                save_datum = econtext->domainValue_datum;
                save_isNull = econtext->domainValue_isNull;

                econtext->domainValue_datum = result;
                econtext->domainValue_isNull = *isNull;

                conResult = ExecEvalExpr(con->check_expr, econtext, &conIsNull, NULL);

                if (!conIsNull && !DatumGetBool(conResult))
                    ereport(ERROR,
                        (errcode(ERRCODE_CHECK_VIOLATION),
                            errmsg("value for domain %s violates check constraint \"%s\"",
                                format_type_be(ctest->resulttype),
                                con->name)));
                econtext->domainValue_datum = save_datum;
                econtext->domainValue_isNull = save_isNull;

                break;
            }
            default:
                ereport(ERROR,
                    (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                        errmodule(MOD_EXECUTOR),
                        errmsg("unrecognized constraint type: %d", (int)con->constrainttype)));
                break;
        }
    }

    /* If all has gone well (constraints did not fail) return the datum */
    return result;
}

/*
 * ExecEvalCoerceToDomainValue
 *
 * Return the value stored by CoerceToDomain.
 */
static Datum ExecEvalCoerceToDomainValue(
    ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    if (isDone != NULL)
        *isDone = ExprSingleResult;
    *isNull = econtext->domainValue_isNull;
    return econtext->domainValue_datum;
}

/* ----------------------------------------------------------------
 *		ExecEvalFieldSelect
 *
 *		Evaluate a FieldSelect node.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalFieldSelect(FieldSelectState* fstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    FieldSelect* fselect = (FieldSelect*)fstate->xprstate.expr;
    AttrNumber fieldnum = fselect->fieldnum;
    Datum result;
    Datum tupDatum;
    HeapTupleHeader tuple;
    Oid tupType;
    int32 tupTypmod;
    TupleDesc tupDesc;
    Form_pg_attribute attr;
    HeapTupleData tmptup;

    tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);

    /* this test covers the isDone exception too: */
    if (*isNull)
        return tupDatum;

    tuple = DatumGetHeapTupleHeader(tupDatum);

    tupType = HeapTupleHeaderGetTypeId(tuple);
    tupTypmod = HeapTupleHeaderGetTypMod(tuple);

    /* Lookup tupdesc if first time through or if type changes */
    tupDesc = get_cached_rowtype(tupType, tupTypmod, &fstate->argdesc, econtext);

    /*
     * Find field's attr record.  Note we don't support system columns here: a
     * datum tuple doesn't have valid values for most of the interesting
     * system columns anyway.
     */
    if (fieldnum <= 0) /* should never happen */
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                errmodule(MOD_EXECUTOR),
                errmsg("unsupported reference to system column %d in FieldSelect", fieldnum)));
    if (fieldnum > tupDesc->natts) /* should never happen */
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_ATTRIBUTE),
                errmodule(MOD_EXECUTOR),
                errmsg("attribute number %d exceeds number of columns %d", fieldnum, tupDesc->natts)));
    attr = tupDesc->attrs[fieldnum - 1];

    /* Check for dropped column, and force a NULL result if so */
    if (attr->attisdropped) {
        *isNull = true;
        return (Datum)0;
    }

    /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
    /* As in ExecEvalScalarVar, we should but can't check typmod */
    if (fselect->resulttype != attr->atttypid)
        ereport(ERROR,
            (errcode(ERRCODE_DATATYPE_MISMATCH),
                errmsg("attribute %d has wrong type", fieldnum),
                errdetail("Table has type %s, but query expects %s.",
                    format_type_be(attr->atttypid),
                    format_type_be(fselect->resulttype))));

    /* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
    tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
    tmptup.t_data = tuple;

    result = tableam_tops_tuple_getattr(&tmptup, fieldnum, tupDesc, isNull);
    return result;
}

/* ----------------------------------------------------------------
 *		ExecEvalFieldStore
 *
 *		Evaluate a FieldStore node.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalFieldStore(FieldStoreState* fstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    FieldStore* fstore = (FieldStore*)fstate->xprstate.expr;
    HeapTuple tuple;
    Datum tupDatum;
    TupleDesc tupDesc;
    Datum* values = NULL;
    bool* isnull = NULL;
    Datum save_datum;
    bool save_isNull = false;
    ListCell* l1 = NULL;
    ListCell* l2 = NULL;
    errno_t rc = EOK;

    tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);

    if (isDone != NULL && *isDone == ExprEndResult)
        return tupDatum;

    /* Lookup tupdesc if first time through or after rescan */
    tupDesc = get_cached_rowtype(fstore->resulttype, -1, &fstate->argdesc, econtext);

    /* Allocate workspace */
    values = (Datum*)palloc(tupDesc->natts * sizeof(Datum));
    isnull = (bool*)palloc(tupDesc->natts * sizeof(bool));

    if (!*isNull) {
        /*
         * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
         * set all the fields in the struct just in case.
         */
        HeapTupleHeader tuphdr;
        HeapTupleData tmptup;

        tuphdr = DatumGetHeapTupleHeader(tupDatum);
        tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
        ItemPointerSetInvalid(&(tmptup.t_self));
        tmptup.t_tableOid = InvalidOid;
        tmptup.t_bucketId = InvalidBktId;
#ifdef PGXC
        tmptup.t_xc_node_id = 0;
#endif
        HeapTupleSetZeroBase(&tmptup);
        tmptup.t_data = tuphdr;

        tableam_tops_deform_tuple(&tmptup, tupDesc, values, isnull);
    } else {
        /* Convert null input tuple into an all-nulls row */
        rc = memset_s(isnull, tupDesc->natts * sizeof(bool), true, tupDesc->natts * sizeof(bool));
        securec_check(rc, "\0", "\0");
    }

    /* Result is never null */
    *isNull = false;

    save_datum = econtext->caseValue_datum;
    save_isNull = econtext->caseValue_isNull;

    forboth(l1, fstate->newvals, l2, fstore->fieldnums)
    {
        ExprState* newval = (ExprState*)lfirst(l1);
        AttrNumber fieldnum = lfirst_int(l2);

        Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);

        /*
         * Use the CaseTestExpr mechanism to pass down the old value of the
         * field being replaced; this is needed in case the newval is itself a
         * FieldStore or ArrayRef that has to obtain and modify the old value.
         * It's safe to reuse the CASE mechanism because there cannot be a
         * CASE between here and where the value would be needed, and a field
         * assignment can't be within a CASE either.  (So saving and restoring
         * the caseValue is just paranoia, but let's do it anyway.)
         */
        econtext->caseValue_datum = values[fieldnum - 1];
        econtext->caseValue_isNull = isnull[fieldnum - 1];

        values[fieldnum - 1] = ExecEvalExpr(newval, econtext, &isnull[fieldnum - 1], NULL);
    }

    econtext->caseValue_datum = save_datum;
    econtext->caseValue_isNull = save_isNull;

    tuple = (HeapTuple)tableam_tops_form_tuple(tupDesc, values, isnull, HEAP_TUPLE);

    pfree_ext(values);
    pfree_ext(isnull);

    return HeapTupleGetDatum(tuple);
}

/* ----------------------------------------------------------------
 *		ExecEvalRelabelType
 *
 *		Evaluate a RelabelType node.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalRelabelType(GenericExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
}

/* ----------------------------------------------------------------
 *		ExecEvalCoerceViaIO
 *
 *		Evaluate a CoerceViaIO node.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalCoerceViaIO(CoerceViaIOState* iostate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    Datum result;
    Datum inputval;
    char* string = NULL;

    inputval = ExecEvalExpr(iostate->arg, econtext, isNull, isDone);

    if (isDone && *isDone == ExprEndResult)
        return inputval; /* nothing to do */

    if (*isNull)
        string = NULL; /* output functions are not called on nulls */
    else
        string = OutputFunctionCall(&iostate->outfunc, inputval);

    result = InputFunctionCall(&iostate->infunc, string, iostate->intypioparam, -1);

    /* The input function cannot change the null/not-null status */
    return result;
}

/* ----------------------------------------------------------------
 *		ExecEvalArrayCoerceExpr
 *
 *		Evaluate an ArrayCoerceExpr node.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalArrayCoerceExpr(
    ArrayCoerceExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    ArrayCoerceExpr* acoerce = (ArrayCoerceExpr*)astate->xprstate.expr;
    Datum result;
    ArrayType* array = NULL;
    FunctionCallInfoData locfcinfo;

    result = ExecEvalExpr(astate->arg, econtext, isNull, isDone);

    if (isDone && *isDone == ExprEndResult)
        return result; /* nothing to do */
    if (*isNull)
        return result; /* nothing to do */

    /*
     * If it's binary-compatible, modify the element type in the array header,
     * but otherwise leave the array as we received it.
     */
    if (!OidIsValid(acoerce->elemfuncid)) {
        /* Detoast input array if necessary, and copy in any case */
        array = DatumGetArrayTypePCopy(result);
        ARR_ELEMTYPE(array) = astate->resultelemtype;
        PG_RETURN_ARRAYTYPE_P(array);
    }

    /* Detoast input array if necessary, but don't make a useless copy */
    array = DatumGetArrayTypeP(result);

    /* Initialize function cache if first time through */
    if (astate->elemfunc.fn_oid == InvalidOid) {
        AclResult aclresult;

        /* Check permission to call function */
        aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(), ACL_EXECUTE);
        if (aclresult != ACLCHECK_OK)
            aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(acoerce->elemfuncid));

        /* Set up the primary fmgr lookup information */
        fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), econtext->ecxt_per_query_memory);
        fmgr_info_set_expr((Node*)acoerce, &(astate->elemfunc));
    }

    /*
     * Use array_map to apply the function to each array element.
     *
     * We pass on the desttypmod and isExplicit flags whether or not the
     * function wants them.
     *
     * Note: coercion functions are assumed to not use collation.
     */
    InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3, InvalidOid, NULL, NULL);
    locfcinfo.arg[0] = PointerGetDatum(array);
    locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
    locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
    locfcinfo.argnull[0] = false;
    locfcinfo.argnull[1] = false;
    locfcinfo.argnull[2] = false;

    return array_map(&locfcinfo, ARR_ELEMTYPE(array), astate->resultelemtype, astate->amstate);
}

/* ----------------------------------------------------------------
 *		ExecEvalCurrentOfExpr
 *
 * The planner must convert CURRENT OF into a TidScan qualification.
 * So, we have to be able to do ExecInitExpr on a CurrentOfExpr,
 * but we shouldn't ever actually execute it.
 * ----------------------------------------------------------------
 */
static Datum ExecEvalCurrentOfExpr(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    ereport(ERROR,
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_EXECUTOR), errmsg("CURRENT OF cannot be executed")));
    return 0; /* keep compiler quiet */
}

/* ----------------------------------------------------------------
 *		ExecEvalPrefixText
 * ----------------------------------------------------------------
 */
static Datum ExecEvalPrefixText(GenericExprState* state, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    PrefixKey* pkey = (PrefixKey*)state->xprstate.expr;
    Datum result = ExecEvalExpr(state->arg, econtext, isNull, isDone);

    if (*isNull) {
        return (Datum)0;
    }

    return PointerGetDatum(text_substring(result, 1, pkey->length, false));
}
/* ----------------------------------------------------------------
 *		ExecEvalPrefixBytea
 * ----------------------------------------------------------------
 */
static Datum ExecEvalPrefixBytea(GenericExprState* state, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    PrefixKey* pkey = (PrefixKey*)state->xprstate.expr;
    Datum result = ExecEvalExpr(state->arg, econtext, isNull, isDone);

    if (*isNull) {
        return (Datum)0;
    }

    return PointerGetDatum(bytea_substring(result, 1, pkey->length, false));
}

/*
 * ExecEvalExprSwitchContext
 *
 * Same as ExecEvalExpr, but get into the right allocation context explicitly.
 */
Datum ExecEvalExprSwitchContext(ExprState* expression, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    Datum retDatum;
    MemoryContext oldContext;

    oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
    retDatum = ExecEvalExpr(expression, econtext, isNull, isDone);
    MemoryContextSwitchTo(oldContext);
    return retDatum;
}

/*
 * ExecInitExpr: prepare an expression tree for execution
 *
 * This function builds and returns an ExprState tree paralleling the given
 * Expr node tree.	The ExprState tree can then be handed to ExecEvalExpr
 * for execution.  Because the Expr tree itself is read-only as far as
 * ExecInitExpr and ExecEvalExpr are concerned, several different executions
 * of the same plan tree can occur concurrently.
 *
 * This must be called in a memory context that will last as long as repeated
 * executions of the expression are needed.  Typically the context will be
 * the same as the per-query context of the associated ExprContext.
 *
 * Any Aggref, WindowFunc, or SubPlan nodes found in the tree are added to the
 * lists of such nodes held by the parent PlanState. Otherwise, we do very
 * little initialization here other than building the state-node tree.	Any
 * nontrivial work associated with initializing runtime info for a node should
 * happen during the first actual evaluation of that node.	(This policy lets
 * us avoid work if the node is never actually evaluated.)
 *
 * Note: there is no ExecEndExpr function; we assume that any resource
 * cleanup needed will be handled by just releasing the memory context
 * in which the state tree is built.  Functions that require additional
 * cleanup work can register a shutdown callback in the ExprContext.
 *
 *	'node' is the root of the expression tree to examine
 *	'parent' is the PlanState node that owns the expression.
 *
 * 'parent' may be NULL if we are preparing an expression that is not
 * associated with a plan tree.  (If so, it can't have aggs or subplans.)
 * This case should usually come through ExecPrepareExpr, not directly here.
 */
ExprState* ExecInitExpr(Expr* node, PlanState* parent)
{
    ExprState* state = NULL;

    gstrace_entry(GS_TRC_ID_ExecInitExpr);
    if (node == NULL) {
        gstrace_exit(GS_TRC_ID_ExecInitExpr);
        return NULL;
    }

    /* Guard against stack overflow due to overly complex expressions */
    check_stack_depth();

    switch (nodeTag(node)) {
        case T_Var:
            /* varattno == InvalidAttrNumber means it's a whole-row Var */
            if (((Var*)node)->varattno == InvalidAttrNumber) {
                WholeRowVarExprState* wstate = makeNode(WholeRowVarExprState);

                wstate->parent = parent;
                wstate->wrv_junkFilter = NULL;
                state = (ExprState*)wstate;
                state->evalfunc = (ExprStateEvalFunc)ExecEvalWholeRowVar;
            } else {
                state = (ExprState*)makeNode(ExprState);
                state->evalfunc = ExecEvalScalarVar;
            }
            break;
        case T_Const:
        case T_UserVar:
        case T_SetVariableExpr:
            state = (ExprState*)makeNode(ExprState);
            state->evalfunc = ExecEvalConst;
            break;
        case T_Param:
            state = (ExprState*)makeNode(ExprState);
            switch (((Param*)node)->paramkind) {
                case PARAM_EXEC:
                    state->evalfunc = ExecEvalParamExec;
                    break;
                case PARAM_EXTERN:
                    state->evalfunc = ExecEvalParamExtern;
                    break;
                default:
                    ereport(ERROR,
                        (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                            errmodule(MOD_EXECUTOR),
                            errmsg("unrecognized paramkind: %d", (int)((Param*)node)->paramkind)));
                    break;
            }
            break;
        case T_CoerceToDomainValue:
            state = (ExprState*)makeNode(ExprState);
            state->evalfunc = ExecEvalCoerceToDomainValue;
            break;
        case T_CaseTestExpr:
            state = (ExprState*)makeNode(ExprState);
            state->evalfunc = ExecEvalCaseTestExpr;
            break;
        case T_Aggref: {
            Aggref* aggref = (Aggref*)node;
            AggrefExprState* astate = makeNode(AggrefExprState);

            astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalAggref;
            if (parent && (IsA(parent, AggState) || IsA(parent, VecAggState))) {
                AggState* aggstate = (AggState*)parent;
                int naggs;

                aggstate->aggs = lcons(astate, aggstate->aggs);
                naggs = ++aggstate->numaggs;

                astate->aggdirectargs = (List*)ExecInitExpr((Expr*)aggref->aggdirectargs, parent);

                astate->args = (List*)ExecInitExpr((Expr*)aggref->args, parent);

                /*
                 * Complain if the aggregate's arguments contain any
                 * aggregates; nested agg functions are semantically
                 * nonsensical.  (This should have been caught earlier,
                 * but we defend against it here anyway.)
                 */
                if (naggs != aggstate->numaggs)
                    ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("aggregate function calls cannot be nested")));
            } else {
                /* planner messed up */
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_AGG), errmodule(MOD_OPT), errmsg("Aggref found in non-Agg plan node")));
            }
            state = (ExprState*)astate;
        } break;
        case T_GroupingFunc: {
            GroupingFunc* grp_node = (GroupingFunc*)node;
            GroupingFuncExprState* grp_state = makeNode(GroupingFuncExprState);
            Agg* agg = NULL;

            if (parent && (IsA(parent, AggState) || IsA(parent, VecAggState))) {
                grp_state->aggstate = (AggState*)parent;

                agg = (Agg*)(parent->plan);

                if (agg->groupingSets)
                    grp_state->clauses = grp_node->cols;
                else
                    grp_state->clauses = NIL;

                state = (ExprState*)grp_state;
                state->evalfunc = (ExprStateEvalFunc)ExecEvalGroupingFuncExpr;
            } else
                ereport(ERROR,
                    (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND),
                        errmodule(MOD_OPT),
                        errmsg("parent of GROUPING is not Agg node")));
        } break;
        case T_GroupingId: {
            GroupingIdExprState* grp_id_state = makeNode(GroupingIdExprState);
            if (parent == NULL || !IsA(parent, AggState) || !IsA(parent->plan, Agg)) {
                ereport(ERROR,
                    (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND),
                        errmodule(MOD_OPT),
                        errmsg("parent of GROUPINGID is not Agg node")));
            }
            grp_id_state->aggstate = (AggState*)parent;
            state = (ExprState*)grp_id_state;
            state->evalfunc = (ExprStateEvalFunc)ExecEvalGroupingIdExpr;
        } break;
        case T_WindowFunc: {
            WindowFunc* wfunc = (WindowFunc*)node;
            WindowFuncExprState* wfstate = makeNode(WindowFuncExprState);

            wfstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalWindowFunc;
            if (parent && (IsA(parent, WindowAggState) || IsA(parent, VecWindowAggState))) {
                WindowAggState* winstate = (WindowAggState*)parent;
                int nfuncs;

                winstate->funcs = lcons(wfstate, winstate->funcs);
                nfuncs = ++winstate->numfuncs;
                if (wfunc->winagg)
                    winstate->numaggs++;

                wfstate->args = (List*)ExecInitExpr((Expr*)wfunc->args, parent);

                /*
                 * Complain if the windowfunc's arguments contain any
                 * windowfuncs; nested window functions are semantically
                 * nonsensical.  (This should have been caught earlier,
                 * but we defend against it here anyway.)
                 */
                if (nfuncs != winstate->numfuncs)
                    ereport(
                        ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("window function calls cannot be nested")));
            } else {
                /* planner messed up */
                ereport(
                    ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("WindowFunc found in non-WindowAgg plan node")));
            }
            state = (ExprState*)wfstate;
        } break;
        case T_ArrayRef: {
            ArrayRef* aref = (ArrayRef*)node;
            ArrayRefExprState* astate = makeNode(ArrayRefExprState);

            astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArrayRef;
            astate->refupperindexpr = (List*)ExecInitExpr((Expr*)aref->refupperindexpr, parent);
            astate->reflowerindexpr = (List*)ExecInitExpr((Expr*)aref->reflowerindexpr, parent);
            astate->refexpr = ExecInitExpr(aref->refexpr, parent);
            astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr, parent);
            /* do one-time catalog lookups for type info */
            astate->refattrlength = get_typlen(aref->refarraytype);
            get_typlenbyvalalign(
                aref->refelemtype, &astate->refelemlength, &astate->refelembyval, &astate->refelemalign);
            state = (ExprState*)astate;
        } break;
        case T_FuncExpr: {
            FuncExpr* funcexpr = (FuncExpr*)node;
            FuncExprState* fstate = makeNode(FuncExprState);
            fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFunc;

            fstate->args = (List*)ExecInitExpr((Expr*)funcexpr->args, parent);
            fstate->func.fn_oid = InvalidOid; /* not initialized */
            state = (ExprState*)fstate;
        } break;
        case T_OpExpr: {
            OpExpr* opexpr = (OpExpr*)node;
            FuncExprState* fstate = makeNode(FuncExprState);

            fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalOper;
            fstate->args = (List*)ExecInitExpr((Expr*)opexpr->args, parent);
            fstate->func.fn_oid = InvalidOid; /* not initialized */
            state = (ExprState*)fstate;
        } break;
        case T_DistinctExpr: {
            DistinctExpr* distinctexpr = (DistinctExpr*)node;
            FuncExprState* fstate = makeNode(FuncExprState);

            fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalDistinct;
            fstate->args = (List*)ExecInitExpr((Expr*)distinctexpr->args, parent);
            fstate->func.fn_oid = InvalidOid; /* not initialized */
            state = (ExprState*)fstate;
        } break;
        case T_NullIfExpr: {
            NullIfExpr* nullifexpr = (NullIfExpr*)node;
            FuncExprState* fstate = makeNode(FuncExprState);

            fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNullIf;
            fstate->args = (List*)ExecInitExpr((Expr*)nullifexpr->args, parent);
            fstate->func.fn_oid = InvalidOid; /* not initialized */
            state = (ExprState*)fstate;
        } break;
        case T_ScalarArrayOpExpr: {
            ScalarArrayOpExpr* opexpr = (ScalarArrayOpExpr*)node;
            ScalarArrayOpExprState* sstate = makeNode(ScalarArrayOpExprState);

            sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalScalarArrayOp;
            sstate->fxprstate.args = (List*)ExecInitExpr((Expr*)opexpr->args, parent);
            sstate->fxprstate.func.fn_oid = InvalidOid; /* not initialized */
            sstate->element_type = InvalidOid;          /* ditto */
            state = (ExprState*)sstate;
        } break;
        case T_BoolExpr: {
            BoolExpr* boolexpr = (BoolExpr*)node;
            BoolExprState* bstate = makeNode(BoolExprState);

            switch (boolexpr->boolop) {
                case AND_EXPR:
                    bstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalAnd;
                    break;
                case OR_EXPR:
                    bstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalOr;
                    break;
                case NOT_EXPR:
                    bstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNot;
                    break;
                default:
                    ereport(ERROR,
                        (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                            errmodule(MOD_OPT),
                            errmsg("unrecognized boolop: %d", (int)boolexpr->boolop)));
                    break;
            }
            bstate->args = (List*)ExecInitExpr((Expr*)boolexpr->args, parent);
            state = (ExprState*)bstate;
        } break;
        case T_SubPlan: {
            SubPlan* subplan = (SubPlan*)node;
            SubPlanState* sstate = NULL;

            if (parent == NULL)
                ereport(ERROR,
                    (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND),
                        errmodule(MOD_OPT),
                        errmsg("SubPlan found with no parent plan")));

            sstate = ExecInitSubPlan(subplan, parent);

            /* Add SubPlanState nodes to parent->subPlan */
            parent->subPlan = lappend(parent->subPlan, sstate);

            state = (ExprState*)sstate;
        } break;
        case T_AlternativeSubPlan: {
            AlternativeSubPlan* asplan = (AlternativeSubPlan*)node;
            AlternativeSubPlanState* asstate = NULL;

            if (parent == NULL)
                ereport(ERROR,
                    (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND),
                        errmodule(MOD_OPT),
                        errmsg("AlternativeSubPlan found with no parent plan")));

            asstate = ExecInitAlternativeSubPlan(asplan, parent);

            state = (ExprState*)asstate;
        } break;
        case T_FieldSelect: {
            FieldSelect* fselect = (FieldSelect*)node;
            FieldSelectState* fstate = makeNode(FieldSelectState);

            fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFieldSelect;
            fstate->arg = ExecInitExpr(fselect->arg, parent);
            fstate->argdesc = NULL;
            state = (ExprState*)fstate;
        } break;
        case T_FieldStore: {
            FieldStore* fstore = (FieldStore*)node;
            FieldStoreState* fstate = makeNode(FieldStoreState);

            fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFieldStore;
            fstate->arg = ExecInitExpr(fstore->arg, parent);
            fstate->newvals = (List*)ExecInitExpr((Expr*)fstore->newvals, parent);
            fstate->argdesc = NULL;
            state = (ExprState*)fstate;
        } break;
        case T_RelabelType: {
            RelabelType* relabel = (RelabelType*)node;
            GenericExprState* gstate = makeNode(GenericExprState);

            gstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalRelabelType;
            gstate->arg = ExecInitExpr(relabel->arg, parent);
            state = (ExprState*)gstate;
        } break;
        case T_CoerceViaIO: {
            CoerceViaIO* iocoerce = (CoerceViaIO*)node;
            CoerceViaIOState* iostate = makeNode(CoerceViaIOState);
            Oid iofunc;
            bool typisvarlena = false;

            iostate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoerceViaIO;
            iostate->arg = ExecInitExpr(iocoerce->arg, parent);
            /* lookup the result type's input function */
            getTypeInputInfo(iocoerce->resulttype, &iofunc, &iostate->intypioparam);
            fmgr_info(iofunc, &iostate->infunc);
            /* lookup the input type's output function */
            getTypeOutputInfo(exprType((Node*)iocoerce->arg), &iofunc, &typisvarlena);
            fmgr_info(iofunc, &iostate->outfunc);
            state = (ExprState*)iostate;
        } break;
        case T_ArrayCoerceExpr: {
            ArrayCoerceExpr* acoerce = (ArrayCoerceExpr*)node;
            ArrayCoerceExprState* astate = makeNode(ArrayCoerceExprState);

            astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArrayCoerceExpr;
            astate->arg = ExecInitExpr(acoerce->arg, parent);
            astate->resultelemtype = get_element_type(acoerce->resulttype);
            if (astate->resultelemtype == InvalidOid)
                ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("target type is not an array")));
            /* Arrays over domains aren't supported yet */
            Assert(getBaseType(astate->resultelemtype) == astate->resultelemtype);
            astate->elemfunc.fn_oid = InvalidOid; /* not initialized */
            astate->amstate = (ArrayMapState*)palloc0(sizeof(ArrayMapState));
            state = (ExprState*)astate;
        } break;
        case T_ConvertRowtypeExpr: {
            ConvertRowtypeExpr* convert = (ConvertRowtypeExpr*)node;
            ConvertRowtypeExprState* cstate = makeNode(ConvertRowtypeExprState);

            cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalConvertRowtype;
            cstate->arg = ExecInitExpr(convert->arg, parent);
            state = (ExprState*)cstate;
        } break;
        case T_CaseExpr: {
            CaseExpr* caseexpr = (CaseExpr*)node;
            CaseExprState* cstate = makeNode(CaseExprState);
            List* outlist = NIL;
            ListCell* l = NULL;

            cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCase;
            cstate->arg = ExecInitExpr(caseexpr->arg, parent);
            foreach (l, caseexpr->args) {
                CaseWhen* when = (CaseWhen*)lfirst(l);
                CaseWhenState* wstate = makeNode(CaseWhenState);

                Assert(IsA(when, CaseWhen));
                wstate->xprstate.evalfunc = NULL; /* not used */
                wstate->xprstate.expr = (Expr*)when;
                wstate->expr = ExecInitExpr(when->expr, parent);
                wstate->result = ExecInitExpr(when->result, parent);
                outlist = lappend(outlist, wstate);
            }
            cstate->args = outlist;
            cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
            state = (ExprState*)cstate;
        } break;
        case T_ArrayExpr: {
            ArrayExpr* arrayexpr = (ArrayExpr*)node;
            ArrayExprState* astate = makeNode(ArrayExprState);
            List* outlist = NIL;
            ListCell* l = NULL;

            astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArray;
            foreach (l, arrayexpr->elements) {
                Expr* e = (Expr*)lfirst(l);
                ExprState* estate = NULL;

                estate = ExecInitExpr(e, parent);
                outlist = lappend(outlist, estate);
            }
            astate->elements = outlist;
            /* do one-time catalog lookup for type info */
            get_typlenbyvalalign(
                arrayexpr->element_typeid, &astate->elemlength, &astate->elembyval, &astate->elemalign);
            state = (ExprState*)astate;
        } break;
        case T_RowExpr: {
            RowExpr* rowexpr = (RowExpr*)node;
            RowExprState* rstate = makeNode(RowExprState);
            Form_pg_attribute* attrs = NULL;
            List* outlist = NIL;
            ListCell* l = NULL;
            int i;

            rstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalRow;
            /* Build tupdesc to describe result tuples */
            if (rowexpr->row_typeid == RECORDOID) {
                /* generic record, use runtime type assignment */
                rstate->tupdesc = ExecTypeFromExprList(rowexpr->args, rowexpr->colnames, TAM_HEAP);
                BlessTupleDesc(rstate->tupdesc);
                /* we won't need to redo this at runtime */
            } else {
                /* it's been cast to a named type, use that */
                rstate->tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
            }
            /* Set up evaluation, skipping any deleted columns */
            Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts);
            attrs = rstate->tupdesc->attrs;
            i = 0;
            foreach (l, rowexpr->args) {
                Expr* e = (Expr*)lfirst(l);
                ExprState* estate = NULL;

                if (!attrs[i]->attisdropped) {
                    /*
                     * Guard against ALTER COLUMN TYPE on rowtype since
                     * the RowExpr was created.  XXX should we check
                     * typmod too?	Not sure we can be sure it'll be the
                     * same.
                     */
                    if (exprType((Node*)e) != attrs[i]->atttypid)
                        ereport(ERROR,
                            (errcode(ERRCODE_DATATYPE_MISMATCH),
                                errmsg("ROW() column has type %s instead of type %s",
                                    format_type_be(exprType((Node*)e)),
                                    format_type_be(attrs[i]->atttypid))));
                } else {
                    /*
                     * Ignore original expression and insert a NULL. We
                     * don't really care what type of NULL it is, so
                     * always make an int4 NULL.
                     */
                    e = (Expr*)makeNullConst(INT4OID, -1, InvalidOid);
                }
                estate = ExecInitExpr(e, parent);
                outlist = lappend(outlist, estate);
                i++;
            }
            rstate->args = outlist;
            state = (ExprState*)rstate;
        } break;
        case T_RowCompareExpr: {
            RowCompareExpr* rcexpr = (RowCompareExpr*)node;
            RowCompareExprState* rstate = makeNode(RowCompareExprState);
            int nopers = list_length(rcexpr->opnos);
            List* outlist = NIL;
            ListCell* l = NULL;
            ListCell* l2 = NULL;
            ListCell* l3 = NULL;
            int i;

            rstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalRowCompare;
            Assert(list_length(rcexpr->largs) == nopers);
            outlist = NIL;
            foreach (l, rcexpr->largs) {
                Expr* e = (Expr*)lfirst(l);
                ExprState* estate = NULL;

                estate = ExecInitExpr(e, parent);
                outlist = lappend(outlist, estate);
            }
            rstate->largs = outlist;
            Assert(list_length(rcexpr->rargs) == nopers);
            outlist = NIL;
            foreach (l, rcexpr->rargs) {
                Expr* e = (Expr*)lfirst(l);
                ExprState* estate = NULL;

                estate = ExecInitExpr(e, parent);
                outlist = lappend(outlist, estate);
            }
            rstate->rargs = outlist;
            Assert(list_length(rcexpr->opfamilies) == nopers);
            rstate->funcs = (FmgrInfo*)palloc(nopers * sizeof(FmgrInfo));
            rstate->collations = (Oid*)palloc(nopers * sizeof(Oid));
            i = 0;
            forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->inputcollids)
            {
                Oid opno = lfirst_oid(l);
                Oid opfamily = lfirst_oid(l2);
                Oid inputcollid = lfirst_oid(l3);
                int strategy;
                Oid lefttype;
                Oid righttype;
                Oid proc;

                get_op_opfamily_properties(opno, opfamily, false, &strategy, &lefttype, &righttype);
                proc = get_opfamily_proc(opfamily, lefttype, righttype, BTORDER_PROC);

                /*
                 * If we enforced permissions checks on index support
                 * functions, we'd need to make a check here.  But the
                 * index support machinery doesn't do that, and neither
                 * does this code.
                 */
                fmgr_info(proc, &(rstate->funcs[i]));
                rstate->collations[i] = inputcollid;
                i++;
            }
            state = (ExprState*)rstate;
        } break;
        case T_CoalesceExpr: {
            CoalesceExpr* coalesceexpr = (CoalesceExpr*)node;
            CoalesceExprState* cstate = makeNode(CoalesceExprState);
            List* outlist = NIL;
            ListCell* l = NULL;

            cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoalesce;
            foreach (l, coalesceexpr->args) {
                Expr* e = (Expr*)lfirst(l);
                ExprState* estate = NULL;

                estate = ExecInitExpr(e, parent);
                outlist = lappend(outlist, estate);
            }
            cstate->args = outlist;
            state = (ExprState*)cstate;
        } break;
        case T_MinMaxExpr: {
            MinMaxExpr* minmaxexpr = (MinMaxExpr*)node;
            MinMaxExprState* mstate = makeNode(MinMaxExprState);
            List* outlist = NIL;
            ListCell* l = NULL;
            TypeCacheEntry* typentry = NULL;

            mstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalMinMax;
            foreach (l, minmaxexpr->args) {
                Expr* e = (Expr*)lfirst(l);
                ExprState* estate = NULL;

                estate = ExecInitExpr(e, parent);
                outlist = lappend(outlist, estate);
            }
            mstate->args = outlist;
            /* Look up the btree comparison function for the datatype */
            typentry = lookup_type_cache(minmaxexpr->minmaxtype, TYPECACHE_CMP_PROC);
            if (!OidIsValid(typentry->cmp_proc))
                ereport(ERROR,
                    (errcode(ERRCODE_UNDEFINED_FUNCTION),
                        errmsg("could not identify a comparison function for type %s",
                            format_type_be(minmaxexpr->minmaxtype))));

            /*
             * If we enforced permissions checks on index support
             * functions, we'd need to make a check here.  But the index
             * support machinery doesn't do that, and neither does this
             * code.
             */
            fmgr_info(typentry->cmp_proc, &(mstate->cfunc));
            state = (ExprState*)mstate;
        } break;
        case T_XmlExpr: {
            XmlExpr* xexpr = (XmlExpr*)node;
            XmlExprState* xstate = makeNode(XmlExprState);
            List* outlist = NIL;
            ListCell* arg = NULL;

            xstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalXml;
            outlist = NIL;
            foreach (arg, xexpr->named_args) {
                Expr* e = (Expr*)lfirst(arg);
                ExprState* estate = NULL;

                estate = ExecInitExpr(e, parent);
                outlist = lappend(outlist, estate);
            }
            xstate->named_args = outlist;

            outlist = NIL;
            foreach (arg, xexpr->args) {
                Expr* e = (Expr*)lfirst(arg);
                ExprState* estate = NULL;

                estate = ExecInitExpr(e, parent);
                outlist = lappend(outlist, estate);
            }
            xstate->args = outlist;

            state = (ExprState*)xstate;
        } break;
        case T_NullTest: {
            NullTest* ntest = (NullTest*)node;
            NullTestState* nstate = makeNode(NullTestState);

            nstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNullTest;
            nstate->arg = ExecInitExpr(ntest->arg, parent);
            nstate->argdesc = NULL;
            state = (ExprState*)nstate;
        } break;
        case T_HashFilter: {
            HashFilter* htest = (HashFilter*)node;
            HashFilterState* hstate = makeNode(HashFilterState);
            List* outlist = NIL;
            ListCell* l = NULL;
            int idx = 0;

            hstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalHashFilter;

            foreach (l, htest->arg) {
                Expr* e = (Expr*)lfirst(l);
                ExprState* estate = NULL;

                estate = ExecInitExpr(e, parent);
                outlist = lappend(outlist, estate);
            }

            hstate->arg = outlist;
            hstate->bucketMap = get_bucketmap_by_execnode(parent->plan->exec_nodes,
                                                          parent->state->es_plannedstmt,
                                                          &hstate->bucketCnt);
            hstate->nodelist = (uint2*)palloc(list_length(htest->nodeList) * sizeof(uint2));
            foreach (l, htest->nodeList)
                hstate->nodelist[idx++] = lfirst_int(l);

            state = (ExprState*)hstate;
        } break;
        case T_BooleanTest: {
            BooleanTest* btest = (BooleanTest*)node;
            GenericExprState* gstate = makeNode(GenericExprState);

            gstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalBooleanTest;
            gstate->arg = ExecInitExpr(btest->arg, parent);
            state = (ExprState*)gstate;
        } break;
        case T_CoerceToDomain: {
            CoerceToDomain* ctest = (CoerceToDomain*)node;
            CoerceToDomainState* cstate = makeNode(CoerceToDomainState);

            cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoerceToDomain;
            cstate->arg = ExecInitExpr(ctest->arg, parent);
            cstate->constraints = GetDomainConstraints(ctest->resulttype);
            state = (ExprState*)cstate;
        } break;
        case T_CurrentOfExpr:
            state = (ExprState*)makeNode(ExprState);
            state->evalfunc = ExecEvalCurrentOfExpr;
            break;
        case T_TargetEntry: {
            TargetEntry* tle = (TargetEntry*)node;
            GenericExprState* gstate = makeNode(GenericExprState);

            gstate->xprstate.evalfunc = NULL; /* not used */
            gstate->arg = ExecInitExpr(tle->expr, parent);
            state = (ExprState*)gstate;
        } break;
        case T_List: {
            List* outlist = NIL;
            ListCell* l = NULL;

            foreach (l, (List*)node) {
                outlist = lappend(outlist, ExecInitExpr((Expr*)lfirst(l), parent));
            }
            /* Don't fall through to the "common" code below */
            gstrace_exit(GS_TRC_ID_ExecInitExpr);
            return (ExprState*)outlist;
        }
        case T_Rownum: {
            RownumState* rnstate = (RownumState*)makeNode(RownumState);
            rnstate->ps = parent;
            state = (ExprState*)rnstate;
            state->evalfunc = (ExprStateEvalFunc)ExecEvalRownum;
        } break;
        case T_PrefixKey: {
            PrefixKey* pkey = (PrefixKey*)node;
            GenericExprState* gstate = makeNode(GenericExprState);
            Oid argtype = exprType((Node*)pkey->arg);

            if (argtype == BYTEAOID || argtype == RAWOID || argtype == BLOBOID) {
                gstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalPrefixBytea;
            } else {
                gstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalPrefixText;
            }
            gstate->arg = ExecInitExpr(pkey->arg, parent);
            state = (ExprState*)gstate;
        } break;
        default:
            ereport(ERROR,
                (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                    errmsg("unrecognized node type: %d when initializing expression.", (int)nodeTag(node))));
            state = NULL; /* keep compiler quiet */
            break;
    }

    /* Common code for all state-node types */
    state->expr = node;

    if (nodeTag(node) != T_TargetEntry)
        state->resultType = exprType((Node*)node);

    gstrace_exit(GS_TRC_ID_ExecInitExpr);
    return state;
}

/*
 * ExecPrepareExpr --- initialize for expression execution outside a normal
 * Plan tree context.
 *
 * This differs from ExecInitExpr in that we don't assume the caller is
 * already running in the EState's per-query context.  Also, we run the
 * passed expression tree through expression_planner() to prepare it for
 * execution.  (In ordinary Plan trees the regular planning process will have
 * made the appropriate transformations on expressions, but for standalone
 * expressions this won't have happened.)
 */
ExprState* ExecPrepareExpr(Expr* node, EState* estate)
{
    ExprState* result = NULL;
    MemoryContext oldcontext;

    oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);

    node = expression_planner(node);

    result = ExecInitExpr(node, NULL);

    MemoryContextSwitchTo(oldcontext);

    return result;
}

/* ----------------------------------------------------------------
 *					 ExecQual / ExecTargetList / ExecProject
 * ----------------------------------------------------------------
 */
/* ----------------------------------------------------------------
 *		ExecQual
 *
 *		Evaluates a conjunctive boolean expression (qual list) and
 *		returns true iff none of the subexpressions are false.
 *		(We also return true if the list is empty.)
 *
 *	If some of the subexpressions yield NULL but none yield FALSE,
 *	then the result of the conjunction is NULL (ie, unknown)
 *	according to three-valued boolean logic.  In this case,
 *	we return the value specified by the "resultForNull" parameter.
 *
 *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
 *	since SQL specifies that tuples with null WHERE results do not
 *	get selected.  On the other hand, callers evaluating constraint
 *	conditions should pass resultForNull=TRUE, since SQL also specifies
 *	that NULL constraint conditions are not failures.
 *
 *	NOTE: it would not be correct to use this routine to evaluate an
 *	AND subclause of a boolean expression; for that purpose, a NULL
 *	result must be returned as NULL so that it can be properly treated
 *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
 *	This routine is only used in contexts where a complete expression
 *	is being evaluated and we know that NULL can be treated the same
 *	as one boolean result or the other.
 *
 * ----------------------------------------------------------------
 */
bool ExecQual(List* qual, ExprContext* econtext, bool resultForNull)
{
    bool result = false;
    MemoryContext oldContext;
    ListCell* l = NULL;

    /*
     * debugging stuff
     */
    EV_printf("ExecQual: qual is ");
    EV_nodeDisplay(qual);
    EV_printf("\n");

    /*
     * Run in short-lived per-tuple context while computing expressions.
     */
    oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);

    /*
     * Evaluate the qual conditions one at a time.	If we find a FALSE result,
     * we can stop evaluating and return FALSE --- the AND result must be
     * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
     * can stop and return FALSE --- the AND result must be FALSE or NULL in
     * that case, and the caller doesn't care which.
     *
     * If we get to the end of the list, we can return TRUE.  This will happen
     * when the AND result is indeed TRUE, or when the AND result is NULL (one
     * or more NULL subresult, with all the rest TRUE) and the caller has
     * specified resultForNull = TRUE.
     */
    result = true;

    foreach (l, qual) {
        ExprState* clause = (ExprState*)lfirst(l);
        Datum expr_value;
        bool isNull = false;

        expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);

        if (isNull) {
            if (resultForNull == false) {
                result = false; /* treat NULL as FALSE */
                break;
            }
        } else {
            if (!DatumGetBool(expr_value)) {
                result = false; /* definitely FALSE */
                break;
            }
        }
    }

    MemoryContextSwitchTo(oldContext);

    return result;
}

/*
 * Number of items in a tlist (including any resjunk items!)
 */
int ExecTargetListLength(List* targetlist)
{
    /* This used to be more complex, but fjoins are dead */
    return list_length(targetlist);
}

/*
 * Number of items in a tlist, not including any resjunk items
 */
int ExecCleanTargetListLength(List* targetlist)
{
    int len = 0;
    ListCell* tl = NULL;

    foreach (tl, targetlist) {
        TargetEntry* curTle = (TargetEntry*)lfirst(tl);

        Assert(IsA(curTle, TargetEntry));
        if (!curTle->resjunk)
            len++;
    }
    return len;
}

static HeapTuple get_tuple(Relation relation, ItemPointer tid)
{
    Buffer user_buf = InvalidBuffer;
    HeapTuple tuple = NULL;
    HeapTuple new_tuple = NULL;

    /* alloc mem for old tuple and set tuple id */
    tuple = (HeapTupleData *)heaptup_alloc(BLCKSZ);
    tuple->t_data = (HeapTupleHeader)((char *)tuple + HEAPTUPLESIZE);
    Assert(tid != NULL);
    tuple->t_self = *tid;
    
    if (heap_fetch(relation, SnapshotAny, tuple, &user_buf, false, NULL)) {
        new_tuple = heapCopyTuple((HeapTuple)tuple, relation->rd_att, NULL);
        ReleaseBuffer(user_buf);
    } else {
        ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("The tuple is not found"),
            errdetail("Another user is getting tuple or the datum is NULL")));
    }

    heap_freetuple(tuple);
    return new_tuple;
}

static void check_huge_clob_paramter(FunctionCallInfoData* fcinfo, bool is_have_huge_clob)
{
    if (!is_have_huge_clob || IsSystemObjOid(fcinfo->flinfo->fn_oid)) {
        return;
    }
    Oid schema_oid = get_func_namespace(fcinfo->flinfo->fn_oid);
    if (IsPackageSchemaOid(schema_oid)) {
        return;
    }
    ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
            errmsg("huge clob do not support as function in parameter")));
}


bool is_external_clob(Oid type_oid, bool is_null, Datum value)
{
    if (type_oid == CLOBOID && !is_null && VARATT_IS_EXTERNAL_LOB(value)) {
        return true;
    }
    return false;
}

bool is_huge_clob(Oid type_oid, bool is_null, Datum value)
{
    if (!is_external_clob(type_oid, is_null, value)) {
        return false;
    }

    struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(value));
    bool is_huge_clob = false;
    /* get relation by relid */
    ItemPointerData tuple_ctid;
    tuple_ctid.ip_blkid.bi_hi = lob_pointer->bi_hi;
    tuple_ctid.ip_blkid.bi_lo = lob_pointer->bi_lo;
    tuple_ctid.ip_posid = lob_pointer->ip_posid;
    Relation relation = heap_open(lob_pointer->relid, RowExclusiveLock);
    HeapTuple origin_tuple = get_tuple(relation, &tuple_ctid);
    if (!HeapTupleIsValid(origin_tuple)) {
        ereport(ERROR,
            (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
            errmsg("cache lookup failed for tuple from relation %u", lob_pointer->relid)));
    }
    bool attr_is_null = false;
    Datum attr = fastgetattr(origin_tuple, lob_pointer->columid, relation->rd_att, &attr_is_null);
    if (!attr_is_null && VARATT_IS_HUGE_TOAST_POINTER(attr)) {
        is_huge_clob = true;
    }
    heap_close(relation, NoLock);
    heap_freetuple(origin_tuple);
    return is_huge_clob;
}

Datum fetch_lob_value_from_tuple(varatt_lob_pointer* lob_pointer, Oid update_oid, bool* is_null)
{
    /* get relation by relid */
    ItemPointerData tuple_ctid;
    tuple_ctid.ip_blkid.bi_hi = lob_pointer->bi_hi;
    tuple_ctid.ip_blkid.bi_lo = lob_pointer->bi_lo;
    tuple_ctid.ip_posid = lob_pointer->ip_posid;
    Relation relation = heap_open(lob_pointer->relid, RowExclusiveLock);
    HeapTuple origin_tuple = get_tuple(relation, &tuple_ctid);
    if (!HeapTupleIsValid(origin_tuple)) {
        ereport(ERROR,
            (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
            errmsg("cache lookup failed for tuple from relation %u", lob_pointer->relid)));
    }

    Datum attr = fastgetattr(origin_tuple, lob_pointer->columid, relation->rd_att, is_null);


    if (!OidIsValid(update_oid)) {
        heap_close(relation, NoLock);
        return attr;
    }
    Datum new_attr = (Datum)0;
    if (*is_null) {
        new_attr = (Datum)0;
    } else {
        if (VARATT_IS_HUGE_TOAST_POINTER(attr)) {
            if (unlikely(origin_tuple->tupTableType == UHEAP_TUPLE)) {
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_NAME),
                        errmsg("UStore cannot update clob column that larger than 1GB")));
            }
            Relation update_rel = heap_open(update_oid, RowExclusiveLock);
            struct varlena *old_value = (struct varlena *)DatumGetPointer(attr);
            struct varlena *new_value = heap_tuple_fetch_and_copy(update_rel, old_value, false);
            new_attr = PointerGetDatum(new_value);
            heap_close(update_rel, NoLock);
        } else if (VARATT_IS_SHORT(attr) || VARATT_IS_EXTERNAL(attr) || VARATT_IS_4B(attr)) {
            new_attr = PointerGetDatum(attr);
        } else {
            ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR),
                    errmsg("lob value which fetch from tuple type is not recognized."), 
                        errdetail("lob type is not one of the existing types")));
        }
    }
    heap_close(relation, NoLock);
    return new_attr;
}

/*
 * Return true if objid is a partition oid, and set relationid to objid's parent relation oid.
 * Return false if objid is a relation oid.
 */
static bool getRelIdForPartition(Oid objid, Oid *relationid)
{
    HeapTuple reltuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objid));
    if (HeapTupleIsValid(reltuple)) {
        ReleaseSysCache(reltuple);
        /* is relation oid */
        return false;
    } else {
        HeapTuple partuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(objid));
        if (HeapTupleIsValid(partuple)) {
            /* is partition oid */
            Form_pg_partition partition = (Form_pg_partition)GETSTRUCT(partuple);
            Oid relid = InvalidOid;
            if (partition->parttype == PART_OBJ_TYPE_TABLE_PARTITION) {
                relid = partition->parentid;
                ReleaseSysCache(partuple);
            } else if (partition->parttype == PART_OBJ_TYPE_TABLE_SUB_PARTITION) {
                Oid partid = partition->parentid;
                ReleaseSysCache(partuple);
                HeapTuple partuple_subparent = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partid));
                if (HeapTupleIsValid(partuple_subparent)) {
                    relid = ((Form_pg_partition)GETSTRUCT(partuple_subparent))->parentid;
                    ReleaseSysCache(partuple_subparent);
                } else {
                    /* wrong sub partition oid */
                    ereport(ERROR, (errcode(ERRCODE_RELATION_OPEN_ERROR),
                                    errmsg("could not fine relation for subpartition OID %u", objid)));
                }
            }
            HeapTuple parenttuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
            if (HeapTupleIsValid(parenttuple)) {
                ReleaseSysCache(parenttuple);
                *relationid = relid;
                return true;
            } else {
                /* wrong partition oid */
                ereport(ERROR, (errcode(ERRCODE_RELATION_OPEN_ERROR),
                                errmsg("could not fine relation for partition OID %u", objid)));
            }
        }
    }
    /* is relation oid */
    return false;
}

/*
 * ExecTargetList
 *		Evaluates a targetlist with respect to the given
 *		expression context.  Returns TRUE if we were able to create
 *		a result, FALSE if we have exhausted a set-valued expression.
 *
 * Results are stored into the passed values and isnull arrays.
 * The caller must provide an itemIsDone array that persists across calls.
 *
 * As with ExecEvalExpr, the caller should pass isDone = NULL if not
 * prepared to deal with sets of result tuples.  Otherwise, a return
 * of *isDone = ExprMultipleResult signifies a set element, and a return
 * of *isDone = ExprEndResult signifies end of the set of tuple.
 * We assume that *isDone has been initialized to ExprSingleResult by caller.
 */
static bool ExecTargetList(List* targetlist, ExprContext* econtext, Datum* values, bool* isnull,
    ExprDoneCond* itemIsDone, ExprDoneCond* isDone)
{
    MemoryContext oldContext;
    bool haveDoneSets = false;

    /*
     * Run in short-lived per-tuple context while computing expressions.
     */
    oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);

    RightRefState* refState = econtext->rightRefState;
    int targetCount = list_length(targetlist);
    GenericExprState* targetArr[targetCount];
    
    int colCnt = (IS_ENABLE_RIGHT_REF(refState) && refState->colCnt > 0) ? refState->colCnt : 1;
    bool hasExecs[colCnt];

    SortTargetListAsArray(refState, targetlist, targetArr);

    InitOutputValues(refState, targetArr, values, isnull, targetCount, hasExecs);
    
    /*
     * evaluate all the expressions in the target list
     */
    haveDoneSets = false; /* any exhausted set exprs in tlist? */

    for (GenericExprState* gstate : targetArr) {
        TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr;
        AttrNumber resind = tle->resno - 1;

        ELOG_FIELD_NAME_START(tle->resname);

        values[resind] = ExecEvalExpr(gstate->arg, econtext, &isnull[resind], &itemIsDone[resind]);
        if (IS_ENABLE_RIGHT_REF(refState) && resind < refState->colCnt) {
            hasExecs[resind] = true;
        }

        if (T_Var == nodeTag(tle->expr) && !isnull[resind]) {
            Var *var = (Var *)tle->expr;
            if (var->vartype == TIDOID) {
                Assert(ItemPointerIsValid((ItemPointer)values[resind]));
            }
        }


        bool isClobAndNotNull = false;
        isClobAndNotNull = (IsA(tle->expr, Param)) && (!isnull[resind]) && (((Param*)tle->expr)->paramtype == CLOBOID
            || ((Param*)tle->expr)->paramtype == BLOBOID);
        if (isClobAndNotNull && econtext->ecxt_scantuple != NULL) {
            /* if is big lob, fetch and copy from toast */
            if (VARATT_IS_HUGE_TOAST_POINTER(values[resind])) {
                Datum new_attr = (Datum)0;
                Oid update_oid = ((HeapTuple)(econtext->ecxt_scantuple->tts_tuple))->t_tableOid;
                Oid parent_oid = InvalidOid;
                Relation parent_rel = NULL;
                Relation part_rel = NULL;
                Partition part = NULL;
                bool ispartition = getRelIdForPartition(update_oid, &parent_oid);
                if (ispartition) {
                    parent_rel = heap_open(parent_oid, RowExclusiveLock);
                    part = partitionOpen(parent_rel, update_oid, RowExclusiveLock);
                    part_rel = partitionGetRelation(parent_rel, part);
                } else {
                    parent_rel = heap_open(update_oid, RowExclusiveLock);
                }
                struct varlena *old_value = (struct varlena *)DatumGetPointer(values[resind]);
                struct varlena *new_value = heap_tuple_fetch_and_copy(part_rel == NULL ? parent_rel : part_rel,
                                                                      old_value, false);
                if (new_value == NULL) {
                    isnull[resind] = true;
                }
                new_attr = PointerGetDatum(new_value);
                if (ispartition) {
                    releaseDummyRelation(&part_rel);
                    partitionClose(parent_rel, part, NoLock);
                    heap_close(parent_rel, NoLock);
                } else {
                    heap_close(parent_rel, NoLock);
                }
                values[resind] = new_attr;
            }
        }
        ELOG_FIELD_NAME_END;

        if (itemIsDone[resind] != ExprSingleResult) {
            /* We have a set-valued expression in the tlist */
            if (isDone == NULL)
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("set-valued function called in context when calculate targetlist that cannot accept a "
                               "set")));
            if (itemIsDone[resind] == ExprMultipleResult) {
                /* we have undone sets in the tlist, set flag */
                *isDone = ExprMultipleResult;
            } else {
                /* we have done sets in the tlist, set flag for that */
                haveDoneSets = true;
            }
        }
    }

    if (haveDoneSets) {
        /*
         * note: can't get here unless we verified isDone != NULL
         */
        if (*isDone == ExprSingleResult) {
            /*
             * all sets are done, so report that tlist expansion is complete.
             */
            *isDone = ExprEndResult;
            MemoryContextSwitchTo(oldContext);
            return false;
        } else {
            /*
             * We have some done and some undone sets.	Restart the done ones
             * so that we can deliver a tuple (if possible).
             */
            for (GenericExprState* gstate : targetArr) {
                TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr;
                AttrNumber resind = tle->resno - 1;

                if (itemIsDone[resind] == ExprEndResult) {
                    values[resind] = ExecEvalExpr(gstate->arg, econtext, &isnull[resind], &itemIsDone[resind]);

                    if (itemIsDone[resind] == ExprEndResult) {
                        /*
                         * Oh dear, this item is returning an empty set. Guess
                         * we can't make a tuple after all.
                         */
                        *isDone = ExprEndResult;
                        break;
                    }
                }
            }

            /*
             * If we cannot make a tuple because some sets are empty, we still
             * have to cycle the nonempty sets to completion, else resources
             * will not be released from subplans etc.
             *
             * XXX is that still necessary?
             */
            if (*isDone == ExprEndResult) {
                for (GenericExprState* gstate : targetArr) {
                    TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr;
                    AttrNumber resind = tle->resno - 1;

                    while (itemIsDone[resind] == ExprMultipleResult) {
                        values[resind] = ExecEvalExpr(gstate->arg, econtext, &isnull[resind], &itemIsDone[resind]);
                    }
                }

                MemoryContextSwitchTo(oldContext);
                return false;
            }
        }
    }


    if (IS_ENABLE_RIGHT_REF(econtext->rightRefState)) {
        econtext->rightRefState->values = nullptr;
        econtext->rightRefState->isNulls = nullptr;
        econtext->rightRefState->hasExecs = nullptr;
    }
    
    /* Report success */
    MemoryContextSwitchTo(oldContext);

    return true;
}

/*
 * ExecProject
 *
 *		projects a tuple based on projection info and stores
 *		it in the previously specified tuple table slot.
 *
 *		Note: the result is always a virtual tuple; therefore it
 *		may reference the contents of the exprContext's scan tuples
 *		and/or temporary results constructed in the exprContext.
 *		If the caller wishes the result to be valid longer than that
 *		data will be valid, he must call ExecMaterializeSlot on the
 *		result slot.
 */
TupleTableSlot* ExecProject(ProjectionInfo* projInfo, ExprDoneCond* isDone)
{
    /*
     * sanity checks
     */
    Assert(projInfo != NULL);

    /*
     * get the projection info we want
     */
    TupleTableSlot *slot = projInfo->pi_slot;
    ExprContext *econtext = projInfo->pi_exprContext;

    /* Assume single result row until proven otherwise */
    if (isDone != NULL)
        *isDone = ExprSingleResult;

    /*
     * Clear any former contents of the result slot.  This makes it safe for
     * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
     * return the slot as-is if we decide no rows can be projected.)
     */
    (void)ExecClearTuple(slot);

    /*
     * Force extraction of all input values that we'll need.  The
     * Var-extraction loops below depend on this, and we are also prefetching
     * all attributes that will be referenced in the generic expressions.
     */
    if (projInfo->pi_lastInnerVar > 0) {
        tableam_tslot_getsomeattrs(econtext->ecxt_innertuple, projInfo->pi_lastInnerVar);
    }

    if (projInfo->pi_lastOuterVar > 0) {
        tableam_tslot_getsomeattrs(econtext->ecxt_outertuple, projInfo->pi_lastOuterVar);
    }

    if (projInfo->pi_lastScanVar > 0 && econtext->ecxt_scantuple) {
    	tableam_tslot_getsomeattrs(econtext->ecxt_scantuple, projInfo->pi_lastScanVar);
    }

    /*
     * Assign simple Vars to result by direct extraction of fields from source
     * slots ... a mite ugly, but fast ...
     */
    int numSimpleVars = projInfo->pi_numSimpleVars;
    if (numSimpleVars > 0 && !IS_ENABLE_RIGHT_REF(projInfo->pi_exprContext->rightRefState)) {
        Datum* values = slot->tts_values;
        bool* isnull = slot->tts_isnull;
        int* varSlotOffsets = projInfo->pi_varSlotOffsets;
        int* varNumbers = projInfo->pi_varNumbers;
        int i;

        if (projInfo->pi_directMap) {
            /* especially simple case where vars go to output in order */
            for (i = 0; i < numSimpleVars; i++) {
                char* slotptr = ((char*)econtext) + varSlotOffsets[i];
                TupleTableSlot* varSlot = *((TupleTableSlot**)slotptr);
                int varNumber = varNumbers[i] - 1;

                Assert (varNumber < varSlot->tts_tupleDescriptor->natts);
                Assert (i < slot->tts_tupleDescriptor->natts);
                values[i] = varSlot->tts_values[varNumber];
                isnull[i] = varSlot->tts_isnull[varNumber];
            }
        } else {
            /* we have to pay attention to varOutputCols[] */
            int* varOutputCols = projInfo->pi_varOutputCols;

            for (i = 0; i < numSimpleVars; i++) {
                char* slotptr = ((char*)econtext) + varSlotOffsets[i];
                TupleTableSlot* varSlot = *((TupleTableSlot**)slotptr);
                int varNumber = varNumbers[i] - 1;
                int varOutputCol = varOutputCols[i] - 1;

                Assert (varNumber < varSlot->tts_tupleDescriptor->natts);
                Assert (varOutputCol < slot->tts_tupleDescriptor->natts);
                values[varOutputCol] = varSlot->tts_values[varNumber];
                isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
            }
        }
    }

    /*
     * If there are any generic expressions, evaluate them.  It's possible
     * that there are set-returning functions in such expressions; if so and
     * we have reached the end of the set, we return the result slot, which we
     * already marked empty.
     */
    if (projInfo->pi_targetlist) {
        if (IS_ENABLE_RIGHT_REF(econtext->rightRefState)) {
            econtext->rightRefState->isUpsert = projInfo->isUpsertHasRightRef;
        }
        bool flag = !ExecTargetList(projInfo->pi_targetlist, econtext, slot->tts_values,
                                    slot->tts_isnull, projInfo->pi_itemIsDone, isDone);

        if (econtext->rightRefState) {
            econtext->rightRefState->isUpsert = false;
        }
        
        if (flag) {
            return slot; /* no more result rows, return empty slot */
        }
    }

    /*
     * Successfully formed a result row.  Mark the result slot as containing a
     * valid virtual tuple.
     */
    return ExecStoreVirtualTuple(slot);
}

static Datum ExecEvalGroupingIdExpr(
    GroupingIdExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
    int groupingId = 0;

    if (isDone != NULL) {
        *isDone = ExprSingleResult;
    }

    *isNull = false;

    for (int i = 0; i < gstate->aggstate->current_phase; i++) {
        groupingId += gstate->aggstate->phases[i].numsets;
    }
    groupingId += gstate->aggstate->projected_set + 1;

    return (Datum)groupingId;
}

/*
 * @Description: copy cursor data from estate->datums to target_cursor
 * @in datums - estate->datums
 * @in dno - varno in datums
 * @in target_cursor - target cursor data
 * @return -void
 */
void ExecCopyDataFromDatum(PLpgSQL_datum** datums, int dno, Cursor_Data* target_cursor)
{
    PLpgSQL_var *cursor_var = (PLpgSQL_var *)(datums[dno]);

    /* only copy cursor option to refcursor */
    if (cursor_var->datatype->typoid != REFCURSOROID) {
        return;
    }

    cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ISOPEN]);
    target_cursor->is_open = DatumGetBool(cursor_var->value);
    cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_FOUND]);
    target_cursor->found = DatumGetBool(cursor_var->value);
    target_cursor->null_fetch = cursor_var->isnull;
    cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_NOTFOUND]);
    target_cursor->not_found = DatumGetBool(cursor_var->value);
    cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ROWCOUNT]);
    target_cursor->row_count = DatumGetInt32(cursor_var->value);
    target_cursor->null_open = cursor_var->isnull;
    target_cursor->cur_dno = dno;
}

/*
 * @Description: copy cursor data to estate->datums
 * @in datums - estate->datums
 * @in dno - varno in datums
 * @in target_cursor - source cursor data
 * @return -void
 */
void ExecCopyDataToDatum(PLpgSQL_datum** datums, int dno, Cursor_Data* source_cursor)
{
    PLpgSQL_var *cursor_var = (PLpgSQL_var *)(datums[dno]);

    /* only copy cursor option to refcursor */
    if (cursor_var->datatype->typoid != REFCURSOROID) {
        return;
    }

    cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ISOPEN]);
    cursor_var->value = BoolGetDatum(source_cursor->is_open);
    cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_FOUND]);
    cursor_var->value = BoolGetDatum(source_cursor->found);
    cursor_var->isnull = source_cursor->null_fetch;
    cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_NOTFOUND]);
    cursor_var->value = BoolGetDatum(source_cursor->not_found);
    cursor_var->isnull = source_cursor->null_fetch;
    cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ROWCOUNT]);
    cursor_var->value = Int32GetDatum(source_cursor->row_count);
    cursor_var->isnull = source_cursor->null_open;
}
